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.indoor.radiosource;
17  
18  import com.irurueta.algebra.AlgebraException;
19  import com.irurueta.algebra.Matrix;
20  import com.irurueta.geometry.Point;
21  import com.irurueta.navigation.LockedException;
22  import com.irurueta.navigation.NotReadyException;
23  import com.irurueta.navigation.indoor.RadioSource;
24  import com.irurueta.navigation.indoor.RadioSourceLocated;
25  import com.irurueta.navigation.indoor.RangingAndRssiReadingLocated;
26  import com.irurueta.navigation.indoor.RangingReadingLocated;
27  import com.irurueta.navigation.indoor.ReadingLocated;
28  import com.irurueta.navigation.indoor.RssiReadingLocated;
29  import com.irurueta.navigation.indoor.Utils;
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   * This is an abstract class to robustly estimate position, transmitted power and path-loss
39   * exponent of a radio source (e.g. Wi-Fi access point or bluetooth beacon), by discarding
40   * outliers and assuming that the ranging data is available to obtain position with
41   * greater accuracy and that the radio source emits isotropically following the
42   * expression below:
43   * Pr = Pt*Gt*Gr*lambda^2 / (4*pi*d)^2,
44   * where Pr is the received power (expressed in mW),
45   * Gt is the Gain of the transmission antenna
46   * Gr is the Gain of the receiver antenna
47   * d is the distance between emitter and receiver
48   * and lambda is the wavelength and is equal to: lambda = c / f,
49   * where c is the speed of light
50   * and f is the carrier frequency of the radio signal.
51   * <p>
52   * Implementations of this class sequentially estimate position and then remaining
53   * parameters. First ranging data is used to robustly estimate position and then
54   * remaining parameters are robustly estimated using former estimated position as
55   * an initial guess.
56   * <p>
57   * Because usually information about the antenna of the radio source cannot be
58   * retrieved (because many measurements are made on unknown devices where
59   * physical access is not possible), this implementation will estimate the
60   * equivalent transmitted power as: Pte = Pt * Gt * Gr.
61   * If Readings contain RSSI standard deviations, those values will be used,
62   * otherwise it will be assumed an RSSI standard deviation of 1 dB.
63   * <p>
64   * This implementation is like SequentialRobustRangingAndRssiRadioSourceEstimator but
65   * allows mixing different kinds of located radio source readings (ranging, RSSI
66   * and ranging+RSSI).
67   *
68   * @param <S> a {@link RadioSource} type.
69   * @param <P> a {@link Point} type.
70   */
71  public abstract class SequentialRobustMixedRadioSourceEstimator<S extends RadioSource, P extends Point<P>> {
72  
73      /**
74       * Default robust estimator method for robust position estimation using ranging
75       * data when no robust method is provided.
76       */
77      public static final RobustEstimatorMethod DEFAULT_PANGING_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
78  
79      /**
80       * Default robust estimator method for path-loss exponent and transmitted power
81       * estimation using RSSI data when no robust method is provided.
82       */
83      public static final RobustEstimatorMethod DEFAULT_RSSI_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
84  
85      /**
86       * Indicates that result is refined by default using all found inliers.
87       */
88      public static final boolean DEFAULT_REFINE_RESULT = true;
89  
90      /**
91       * Indicates that covariance is kept by default after refining result.
92       */
93      public static final boolean DEFAULT_KEEP_COVARIANCE = true;
94  
95      /**
96       * Default amount of progress variation before notifying a change in estimation progress.
97       * By default, this is set to 5%.
98       */
99      public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
100 
101     /**
102      * Minimum allowed value for progress delta.
103      */
104     public static final float MIN_PROGRESS_DELTA = 0.0f;
105 
106     /**
107      * Maximum allowed value for progress delta.
108      */
109     public static final float MAX_PROGRESS_DELTA = 1.0f;
110 
111     /**
112      * Constant defining default confidence of the estimated result, which is
113      * 99%. This means that with a probability of 99% estimation will be
114      * accurate because chosen sub-samples will be inliers.
115      */
116     public static final double DEFAULT_CONFIDENCE = 0.99;
117 
118     /**
119      * Default maximum allowed number of iterations.
120      */
121     public static final int DEFAULT_MAX_ITERATIONS = 5000;
122 
123     /**
124      * Minimum allowed confidence value.
125      */
126     public static final double MIN_CONFIDENCE = 0.0;
127 
128     /**
129      * Maximum allowed confidence value.
130      */
131     public static final double MAX_CONFIDENCE = 1.0;
132 
133     /**
134      * Minimum allowed number of iterations.
135      */
136     public static final int MIN_ITERATIONS = 1;
137 
138     /**
139      * Indicates that by default position covariances of readings must be taken into account to increase
140      * the amount of standard deviation of each ranging measure by the amount of position standard deviation
141      * assuming that both measures are statistically independent.
142      */
143     public static final boolean DEFAULT_USE_READING_POSITION_COVARIANCES = true;
144 
145     /**
146      * Internal robust estimator for position estimation.
147      */
148     protected RobustRangingRadioSourceEstimator<S, P> rangingEstimator;
149 
150     /**
151      * Internal robust estimator for path-loss exponent and transmitted power
152      * estimation.
153      */
154     protected RobustRssiRadioSourceEstimator<S, P> rssiEstimator;
155 
156     /**
157      * Robust method used for robust position estimation using ranging data.
158      */
159     protected RobustEstimatorMethod rangingRobustMethod = DEFAULT_PANGING_ROBUST_METHOD;
160 
161     /**
162      * Robust method used for path-loss exponent and transmitted power estimation
163      * using RSSI data.
164      */
165     protected RobustEstimatorMethod rssiRobustMethod = DEFAULT_RSSI_ROBUST_METHOD;
166 
167     /**
168      * Size of subsets to be checked during ranging robust estimation.
169      */
170     protected int rangingPreliminarySubsetSize;
171 
172     /**
173      * Size of subsets to be checked during RSSI robust estimation.
174      */
175     protected int rssiPreliminarySubsetSize;
176 
177     /**
178      * Threshold to determine when samples are inliers or not used during robust
179      * position estimation.
180      * If not defined, default threshold will be used.
181      */
182     protected Double rangingThreshold;
183 
184     /**
185      * Threshold to determine when samples are inliers or not used during robust
186      * path-loss exponent and transmitted power estimation.
187      */
188     protected Double rssiThreshold;
189 
190     /**
191      * Indicates whether position is estimated using RSSI data.
192      * If enough ranging readings are available, this is false and position is estimated using ranging readings,
193      * otherwise this is true and position is estimated using RSSI data in a less reliable way.
194      */
195     protected boolean rssiPositionEnabled;
196 
197     /**
198      * Signal readings belonging to the same radio source to be estimated.
199      */
200     private List<? extends ReadingLocated<P>> readings;
201 
202     /**
203      * Quality scores corresponding to each provided sample.
204      * The larger the score value the better the quality of the sample.
205      */
206     private double[] qualityScores;
207 
208     /**
209      * Listener to be notified of events such as when estimation starts, ends or its
210      * progress significantly changes.
211      */
212     private SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener;
213 
214     /**
215      * Estimated position.
216      */
217     private P estimatedPosition;
218 
219     /**
220      * Indicates if this instance is locked because estimation is being executed.
221      */
222     private boolean locked;
223 
224     /**
225      * Amount of progress variation before notifying a progress change during estimation.
226      */
227     private float progressDelta = DEFAULT_PROGRESS_DELTA;
228 
229     /**
230      * Amount of confidence expressed as a value between 0.0 and 1.0 (which is equivalent
231      * to 100%) for robust position estimation. The amount of confidence indicates the
232      * probability that the estimated result is correct. Usually this value will be
233      * close to 1.0, but not exactly 1.0.
234      */
235     private double rangingConfidence = DEFAULT_CONFIDENCE;
236 
237     /**
238      * Amount of confidence expressed as a value between 0.0 and 1.0 (which is equivalent
239      * to 100%) for robust path-loss exponent and transmitted power estimation. The amount
240      * of confidence indicates the probability that the estimated result is correct.
241      * Usually this value will be close to 1.0, but not exactly 1.0.
242      */
243     private double rssiConfidence = DEFAULT_CONFIDENCE;
244 
245     /**
246      * Maximum allowed number of iterations for robust position estimation. When the
247      * maximum number of iterations is exceeded, an approximate result might be
248      * available for retrieval.
249      */
250     private int rangingMaxIterations = DEFAULT_MAX_ITERATIONS;
251 
252     /**
253      * Maximum allowed number of iterations for robust path-loss exponent and transmitted
254      * power estimation. When the maximum number of iterations is exceeded, an
255      * approximate result might be available for retrieval.
256      */
257     private int rssiMaxIterations = DEFAULT_MAX_ITERATIONS;
258 
259     /**
260      * Indicates whether result must be refined using found inliers.
261      * If true, inliers will be computed and kept in any implementation regardless of the
262      * settings.
263      */
264     private boolean refineResult = DEFAULT_REFINE_RESULT;
265 
266     /**
267      * Indicates whether covariance must be kept after refining result.
268      * This setting is only taken into account if result is refined.
269      */
270     private boolean keepCovariance = DEFAULT_KEEP_COVARIANCE;
271 
272     /**
273      * Covariance of estimated position, power and/or path-loss exponent.
274      * This is only available when result has been refined and covariance is kept.
275      */
276     private Matrix covariance;
277 
278     /**
279      * Covariance of estimated position.
280      * Size of this matrix will depend on the number of dimensions
281      * of estimated position (either 2 or 3).
282      * This value will only be available when position estimation is enabled.
283      */
284     private Matrix estimatedPositionCovariance;
285 
286     /**
287      * Initially transmitted power to start the estimation of radio source
288      * transmitted power.
289      * If not defined, average value of received power readings will be used.
290      */
291     private Double initialTransmittedPowerdBm;
292 
293     /**
294      * Initial position to start the estimation of radio source position.
295      * If not defined, centroid of provided located readings will be used.
296      */
297     private P initialPosition;
298 
299     /**
300      * Initial exponent typically used on free space for path loss propagation in
301      * terms of distance.
302      * On different environments path loss exponent might have different values:
303      * - Free space: 2.0
304      * - Urban Area: 2.7 to 3.5
305      * - Suburban Area: 3 to 5
306      * - Indoor (line-of-sight): 1.6 to 1.8
307      * <p>
308      * If path loss exponent estimation is enabled, estimation will start at this
309      * value and will converge to the most appropriate value.
310      * If path loss exponent estimation is disabled, this value will be assumed
311      * to be exact and the estimated path loss exponent will be equal to this
312      * value.
313      */
314     private double initialPathLossExponent = MixedRadioSourceEstimator.DEFAULT_PATH_LOSS_EXPONENT;
315 
316     /**
317      * Indicates whether transmitted power estimation is enabled or not.
318      */
319     private boolean transmittedPowerEstimationEnabled =
320             MixedRadioSourceEstimator.DEFAULT_TRANSMITTED_POWER_ESTIMATION_ENABLED;
321 
322     /**
323      * Indicates whether path loss estimation is enabled or not.
324      */
325     private boolean pathLossEstimationEnabled = MixedRadioSourceEstimator.DEFAULT_PATHLOSS_ESTIMATION_ENABLED;
326 
327     /**
328      * Estimated transmitted power expressed in dBm's or null if not available.
329      */
330     private Double estimatedTransmittedPowerdBm;
331 
332     /**
333      * Estimated exponent typically used on free space for path loss propagation in
334      * terms of distance.
335      * On different environments path loss exponent might have different values:
336      * - Free space: 2.0
337      * - Urban Area: 2.7 to 3.5
338      * - Suburban Area: 3 to 5
339      * - Indoor (line-of-sight): 1.6 to 1.8
340      * If path loss exponent estimation is not enabled, this value will always be equal to
341      * {@link RssiRadioSourceEstimator#DEFAULT_PATH_LOSS_EXPONENT}
342      */
343     private double estimatedPathLossExponent = MixedRadioSourceEstimator.DEFAULT_PATH_LOSS_EXPONENT;
344 
345     /**
346      * Variance of estimated transmitted power.
347      * This value will only be available when transmitted power
348      * estimation is enabled.
349      */
350     private Double estimatedTransmittedPowerVariance;
351 
352     /**
353      * Variance of estimated path loss exponent.
354      * This value will only be available when path-loss
355      * exponent estimation is enabled.
356      */
357     private Double estimatedPathLossExponentVariance;
358 
359     /**
360      * Data related to inliers found after estimation.
361      */
362     private InliersData inliersData;
363 
364     /**
365      * Indicates whether position covariances of readings must be taken into account to increase
366      * the amount of standard deviation of each ranging measure by the amount of position standard deviation
367      * assuming that both measures are statistically independent.
368      */
369     private boolean useReadingPositionCovariances = DEFAULT_USE_READING_POSITION_COVARIANCES;
370 
371     /**
372      * Indicates whether an homogeneous ranging linear solver is used to estimate preliminary positions.
373      */
374     private boolean useHomogeneousRangingLinearSolver =
375             RangingRadioSourceEstimator.DEFAULT_USE_HOMOGENEOUS_LINEAR_SOLVER;
376 
377     /**
378      * Number of ranging readings available among all readings.
379      */
380     private int numRangingReadings;
381 
382     /**
383      * Number of RSSI readings available among all readings.
384      */
385     private int numRssiReadings;
386 
387     /**
388      * Constructor.
389      */
390     protected SequentialRobustMixedRadioSourceEstimator() {
391     }
392 
393     /**
394      * Constructor.
395      * Sets signal readings belonging to the same radio source.
396      *
397      * @param readings signal readings belonging to the same radio source.
398      * @throws IllegalArgumentException if readings are not valid.
399      */
400     protected SequentialRobustMixedRadioSourceEstimator(final List<? extends ReadingLocated<P>> readings) {
401         internalSetReadings(readings);
402     }
403 
404     /**
405      * Constructor.
406      *
407      * @param listener listener in charge of attending events raised by this instance.
408      */
409     protected SequentialRobustMixedRadioSourceEstimator(
410             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
411         this.listener = listener;
412     }
413 
414     /**
415      * Constructor.
416      * Sets signal readings belonging to the same radio source.
417      *
418      * @param readings signal readings belonging to the same radio source.
419      * @param listener listener in charge of attending events raised by this instance.
420      * @throws IllegalArgumentException if readings are not valid.
421      */
422     protected SequentialRobustMixedRadioSourceEstimator(
423             final List<? extends ReadingLocated<P>> readings,
424             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
425         this(readings);
426         this.listener = listener;
427     }
428 
429     /**
430      * Constructor.
431      * Sets signal readings belonging to the same radio source.
432      *
433      * @param readings        signal readings belonging to the same radio source.
434      * @param initialPosition initial position to start the estimation of radio
435      *                        source position.
436      * @throws IllegalArgumentException if readings are not valid.
437      */
438     protected SequentialRobustMixedRadioSourceEstimator(
439             final List<? extends ReadingLocated<P>> readings, final P initialPosition) {
440         this(readings);
441         this.initialPosition = initialPosition;
442     }
443 
444     /**
445      * Constructor.
446      *
447      * @param initialPosition initial position to start the estimation of radio
448      *                        source position.
449      */
450     protected SequentialRobustMixedRadioSourceEstimator(final P initialPosition) {
451         this.initialPosition = initialPosition;
452     }
453 
454     /**
455      * Constructor.
456      *
457      * @param initialPosition initial position to start the estimation of radio
458      *                        source position.
459      * @param listener        listener in charge of attending events raised by this instance.
460      */
461     protected SequentialRobustMixedRadioSourceEstimator(
462             final P initialPosition, final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
463         this(listener);
464         this.initialPosition = initialPosition;
465     }
466 
467     /**
468      * Constructor.
469      * Sets signal readings belonging to the same radio source.
470      *
471      * @param readings        signal readings belonging to the same radio source.
472      * @param initialPosition initial position to start the estimation of radio
473      *                        source position.
474      * @param listener        listener in charge of attending events raised by this instance.
475      * @throws IllegalArgumentException if readings are not valid.
476      */
477     protected SequentialRobustMixedRadioSourceEstimator(
478             final List<? extends ReadingLocated<P>> readings, final P initialPosition,
479             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
480         this(readings, listener);
481         this.initialPosition = initialPosition;
482     }
483 
484     /**
485      * Constructor.
486      *
487      * @param initialTransmittedPowerdBm initial transmitted power to start the
488      *                                   estimation of radio source transmitted power
489      *                                   (expressed in dBm's).
490      */
491     protected SequentialRobustMixedRadioSourceEstimator(final Double initialTransmittedPowerdBm) {
492         this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
493     }
494 
495     /**
496      * Constructor.
497      * Sets signal readings belonging to the same radio source.
498      *
499      * @param readings                   signal readings belonging to the same radio source.
500      * @param initialTransmittedPowerdBm initial transmitted power to start the
501      *                                   estimation of radio source transmitted power
502      *                                   (expressed in dBm's).
503      * @throws IllegalArgumentException if readings are not valid.
504      */
505     protected SequentialRobustMixedRadioSourceEstimator(
506             final List<? extends ReadingLocated<P>> readings, final Double initialTransmittedPowerdBm) {
507         this(readings);
508         this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
509     }
510 
511     /**
512      * Constructor.
513      *
514      * @param initialTransmittedPowerdBm initial transmitted power to start the
515      *                                   estimation of radio source transmitted power
516      *                                   (expressed in dBm's).
517      * @param listener                   listener in charge of attending events raised by this instance.
518      */
519     protected SequentialRobustMixedRadioSourceEstimator(
520             final Double initialTransmittedPowerdBm,
521             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
522         this(listener);
523         this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
524     }
525 
526     /**
527      * Constructor.
528      * Sets signal readings belonging to the same radio source.
529      *
530      * @param readings                   signal readings belonging to the same radio source.
531      * @param initialTransmittedPowerdBm initial transmitted power to start the
532      *                                   estimation of radio source transmitted power
533      *                                   (expressed in dBm's).
534      * @param listener                   listener in charge of attending events raised by this instance.
535      * @throws IllegalArgumentException if readings are not valid.
536      */
537     protected SequentialRobustMixedRadioSourceEstimator(
538             final List<? extends ReadingLocated<P>> readings, final Double initialTransmittedPowerdBm,
539             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
540         this(readings, listener);
541         this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
542     }
543 
544     /**
545      * Constructor.
546      * Sets signal readings belonging to the same radio source.
547      *
548      * @param readings                   signal readings belonging to the same radio source.
549      * @param initialPosition            initial position to start the estimation of radio
550      *                                   source position.
551      * @param initialTransmittedPowerdBm initial transmitted power to start the
552      *                                   estimation of radio source transmitted power
553      *                                   (expressed in dBm's).
554      * @throws IllegalArgumentException if readings are not valid.
555      */
556     protected SequentialRobustMixedRadioSourceEstimator(
557             final List<? extends ReadingLocated<P>> readings, final P initialPosition,
558             final Double initialTransmittedPowerdBm) {
559         this(readings);
560         this.initialPosition = initialPosition;
561         this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
562     }
563 
564     /**
565      * Constructor.
566      *
567      * @param initialPosition            initial position to start the estimation of radio
568      *                                   source position.
569      * @param initialTransmittedPowerdBm initial transmitted power to start the
570      *                                   estimation of radio source transmitted power
571      *                                   (expressed in dBm's).
572      */
573     protected SequentialRobustMixedRadioSourceEstimator(
574             final P initialPosition, final Double initialTransmittedPowerdBm) {
575         this.initialPosition = initialPosition;
576         this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
577     }
578 
579     /**
580      * Constructor.
581      *
582      * @param initialPosition            initial position to start the estimation of radio
583      *                                   source position.
584      * @param initialTransmittedPowerdBm initial transmitted power to start the
585      *                                   estimation of radio source transmitted power
586      *                                   (expressed in dBm's).
587      * @param listener                   listener in charge of attending events raised by this instance.
588      */
589     protected SequentialRobustMixedRadioSourceEstimator(
590             final P initialPosition, final Double initialTransmittedPowerdBm,
591             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
592         this(listener);
593         this.initialPosition = initialPosition;
594         this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
595     }
596 
597     /**
598      * Constructor.
599      * Sets signal readings belonging to the same radio source.
600      *
601      * @param readings                   signal readings belonging to the same radio source.
602      * @param initialPosition            initial position to start the estimation of radio
603      *                                   source position.
604      * @param initialTransmittedPowerdBm initial transmitted power to start the
605      *                                   estimation of radio source transmitted power
606      *                                   (expressed in dBm's).
607      * @param listener                   listener in charge of attending events raised by this instance.
608      * @throws IllegalArgumentException if readings are not valid.
609      */
610     protected SequentialRobustMixedRadioSourceEstimator(
611             final List<? extends ReadingLocated<P>> readings, final P initialPosition,
612             final Double initialTransmittedPowerdBm,
613             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
614         this(readings, listener);
615         this.initialPosition = initialPosition;
616         this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
617     }
618 
619     /**
620      * Constructor.
621      * Sets signal readings belonging to the same radio source.
622      *
623      * @param readings                   signal readings belonging to the same radio source.
624      * @param initialPosition            initial position to start the estimation of radio
625      *                                   source position.
626      * @param initialTransmittedPowerdBm initial transmitted power to start the
627      *                                   estimation of radio source transmitted power
628      *                                   (expressed in dBm's).
629      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
630      * @throws IllegalArgumentException if readings are not valid.
631      */
632     protected SequentialRobustMixedRadioSourceEstimator(
633             final List<? extends ReadingLocated<P>> readings, final P initialPosition,
634             final Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
635         this(readings, initialPosition, initialTransmittedPowerdBm);
636         this.initialPathLossExponent = initialPathLossExponent;
637     }
638 
639     /**
640      * Constructor.
641      *
642      * @param initialPosition            initial position to start the estimation of radio
643      *                                   source position.
644      * @param initialTransmittedPowerdBm initial transmitted power to start the
645      *                                   estimation of radio source transmitted power
646      *                                   (expressed in dBm's).
647      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
648      */
649     protected SequentialRobustMixedRadioSourceEstimator(
650             final P initialPosition, final Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
651         this(initialPosition, initialTransmittedPowerdBm);
652         this.initialPathLossExponent = initialPathLossExponent;
653     }
654 
655     /**
656      * Constructor.
657      *
658      * @param initialPosition            initial position to start the estimation of radio
659      *                                   source position.
660      * @param initialTransmittedPowerdBm initial transmitted power to start the
661      *                                   estimation of radio source transmitted power
662      *                                   (expressed in dBm's).
663      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
664      * @param listener                   listener in charge of attending events raised by this instance.
665      */
666     protected SequentialRobustMixedRadioSourceEstimator(
667             final P initialPosition, final Double initialTransmittedPowerdBm, final double initialPathLossExponent,
668             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
669         this(initialPosition, initialTransmittedPowerdBm, listener);
670         this.initialPathLossExponent = initialPathLossExponent;
671     }
672 
673     /**
674      * Constructors.
675      * Sets signal readings belonging to the same radio source.
676      *
677      * @param readings                   signal readings belonging to the same radio source.
678      * @param initialPosition            initial position to start the estimation of radio
679      *                                   source position.
680      * @param initialTransmittedPowerdBm initial transmitted power to start the
681      *                                   estimation of radio source transmitted power
682      *                                   (expressed in dBm's).
683      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
684      * @param listener                   listener in charge of attending events raised by this instance.
685      * @throws IllegalArgumentException if readings are not valid.
686      */
687     protected SequentialRobustMixedRadioSourceEstimator(
688             final List<? extends ReadingLocated<P>> readings, final P initialPosition,
689             final Double initialTransmittedPowerdBm, final double initialPathLossExponent,
690             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
691         this(readings, initialPosition, initialTransmittedPowerdBm, listener);
692         this.initialPathLossExponent = initialPathLossExponent;
693     }
694 
695     /**
696      * Constructor.
697      *
698      * @param qualityScores quality scores corresponding to each provided sample.
699      *                      The larger the score value the better the quality of
700      *                      the sample.
701      * @throws IllegalArgumentException if quality scores is null, or length of
702      *                                  quality scores is less than required minimum.
703      */
704     protected SequentialRobustMixedRadioSourceEstimator(final double[] qualityScores) {
705         this();
706         internalSetQualityScores(qualityScores);
707     }
708 
709     /**
710      * Constructor.
711      * Sets signal readings belonging to the same radio source.
712      *
713      * @param qualityScores quality scores corresponding to each provided sample.
714      *                      The larger the score value the better the quality of
715      *                      the sample.
716      * @param readings      signal readings belonging to the same radio source.
717      * @throws IllegalArgumentException if readings are not valid, quality scores is
718      *                                  null, or length of quality scores is less than required minimum.
719      */
720     protected SequentialRobustMixedRadioSourceEstimator(
721             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings) {
722         this(readings);
723         internalSetQualityScores(qualityScores);
724     }
725 
726     /**
727      * Constructor.
728      *
729      * @param qualityScores quality scores corresponding to each provided sample.
730      *                      The larger the score value the better the quality of
731      *                      the sample.
732      * @param listener      listener in charge of attending events raised by this instance.
733      * @throws IllegalArgumentException if quality scores is null, or length
734      *                                  of quality scores is less than required minimum.
735      */
736     protected SequentialRobustMixedRadioSourceEstimator(
737             final double[] qualityScores, final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
738         this(listener);
739         internalSetQualityScores(qualityScores);
740     }
741 
742     /**
743      * Constructor.
744      * Sets signal readings belonging to the same radio source.
745      *
746      * @param qualityScores quality scores corresponding to each provided sample.
747      *                      The larger the score value the better the quality of
748      *                      the sample.
749      * @param readings      signal readings belonging to the same radio source.
750      * @param listener      listener in charge of attending events raised by this instance.
751      * @throws IllegalArgumentException if readings are not valid, quality scores is
752      *                                  null, or length of quality scores is less than required minimum.
753      */
754     protected SequentialRobustMixedRadioSourceEstimator(
755             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings,
756             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
757         this(readings, listener);
758         internalSetQualityScores(qualityScores);
759     }
760 
761     /**
762      * Constructor.
763      * Sets signal readings belonging to the same radio source.
764      *
765      * @param qualityScores   quality scores corresponding to each provided sample.
766      *                        The larger the score value the better the quality of
767      *                        the sample.
768      * @param readings        signal readings belonging to the same radio source.
769      * @param initialPosition initial position to start the estimation of radio
770      *                        source position.
771      * @throws IllegalArgumentException if readings are not valid, quality scores is
772      *                                  null, or length of quality scores is less than required minimum.
773      */
774     protected SequentialRobustMixedRadioSourceEstimator(
775             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings, final P initialPosition) {
776         this(readings, initialPosition);
777         internalSetQualityScores(qualityScores);
778     }
779 
780     /**
781      * Constructor.
782      *
783      * @param qualityScores   quality scores corresponding to each provided sample.
784      *                        The larger the score value the better the quality of
785      *                        the sample.
786      * @param initialPosition initial position to start the estimation of radio
787      *                        source position.
788      * @throws IllegalArgumentException if quality scores is null, or length
789      *                                  of quality scores is less than required minimum.
790      */
791     protected SequentialRobustMixedRadioSourceEstimator(final double[] qualityScores, final P initialPosition) {
792         this(initialPosition);
793         internalSetQualityScores(qualityScores);
794     }
795 
796     /**
797      * Constructor.
798      *
799      * @param qualityScores   quality scores corresponding to each provided sample.
800      *                        The larger the score value the better the quality of
801      *                        the sample.
802      * @param initialPosition initial position to start the estimation of radio
803      *                        source position.
804      * @param listener        listener in charge of attending events raised by this instance.
805      * @throws IllegalArgumentException if quality scores is null, or length
806      *                                  of quality scores is less than required minimum.
807      */
808     protected SequentialRobustMixedRadioSourceEstimator(
809             final double[] qualityScores, final P initialPosition,
810             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
811         this(initialPosition, listener);
812         internalSetQualityScores(qualityScores);
813     }
814 
815     /**
816      * Constructor.
817      * Sets signal readings belonging to the same radio source.
818      *
819      * @param qualityScores   quality scores corresponding to each provided sample.
820      *                        The larger the score value the better the quality of
821      *                        the sample.
822      * @param readings        signal readings belonging to the same radio source.
823      * @param initialPosition initial position to start the estimation of radio
824      *                        source position.
825      * @param listener        listener in charge of attending events raised by this instance.
826      * @throws IllegalArgumentException if readings are not valid, quality scores
827      *                                  is null, or length of quality scores is less than required minimum.
828      */
829     protected SequentialRobustMixedRadioSourceEstimator(
830             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings,
831             final P initialPosition, final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
832         this(readings, initialPosition, listener);
833         internalSetQualityScores(qualityScores);
834     }
835 
836     /**
837      * Constructor.
838      *
839      * @param qualityScores              quality scores corresponding to each provided sample.
840      *                                   The larger the score value the better the quality of
841      *                                   the sample.
842      * @param initialTransmittedPowerdBm initial transmitted power to start the
843      *                                   estimation of radio source transmitted power
844      *                                   (expressed in dBm's).
845      * @throws IllegalArgumentException if quality scores is null, or length
846      *                                  of quality scores is less than required minimum.
847      */
848     protected SequentialRobustMixedRadioSourceEstimator(
849             final double[] qualityScores, final Double initialTransmittedPowerdBm) {
850         this(initialTransmittedPowerdBm);
851         internalSetQualityScores(qualityScores);
852     }
853 
854     /**
855      * Constructor.
856      * Sets signal readings belonging to the same radio source.
857      *
858      * @param qualityScores              quality scores corresponding to each provided sample.
859      *                                   The larger the score value the better the quality of
860      *                                   the sample.
861      * @param readings                   signal readings belonging to the same radio source.
862      * @param initialTransmittedPowerdBm initial transmitted power to start the
863      *                                   estimation of radio source transmitted power
864      *                                   (expressed in dBm's).
865      * @throws IllegalArgumentException if readings are not valid, quality scores
866      *                                  is null, or length of quality scores is less than required minimum.
867      */
868     protected SequentialRobustMixedRadioSourceEstimator(
869             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings,
870             final Double initialTransmittedPowerdBm) {
871         this(readings, initialTransmittedPowerdBm);
872         internalSetQualityScores(qualityScores);
873     }
874 
875     /**
876      * Constructor.
877      *
878      * @param qualityScores              quality scores corresponding to each provided sample.
879      *                                   The larger the score value the better the quality of
880      *                                   the sample.
881      * @param initialTransmittedPowerdBm initial transmitted power to start the
882      *                                   estimation of radio source transmitted power
883      *                                   (expressed in dBm's).
884      * @param listener                   listener in charge of attending events raised by this instance.
885      * @throws IllegalArgumentException if quality scores is null, or length
886      *                                  of quality scores is less than required minimum.
887      */
888     protected SequentialRobustMixedRadioSourceEstimator(
889             final double[] qualityScores, final Double initialTransmittedPowerdBm,
890             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
891         this(initialTransmittedPowerdBm, listener);
892         internalSetQualityScores(qualityScores);
893     }
894 
895     /**
896      * Constructor.
897      * Sets signal readings belonging to the same radio source.
898      *
899      * @param qualityScores              quality scores corresponding to each provided
900      *                                   sample. The larger the score value the better
901      *                                   the quality of the sample.
902      * @param readings                   signal readings belonging to the same radio source.
903      * @param initialTransmittedPowerdBm initial transmitted power to start the
904      *                                   estimation of radio source transmitted power
905      *                                   (expressed in dBm's).
906      * @param listener                   listener in charge of attending events raised by this instance.
907      * @throws IllegalArgumentException if readings are not valid, quality scores
908      *                                  is null, or length of quality scores is less than required minimum.
909      */
910     protected SequentialRobustMixedRadioSourceEstimator(
911             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings,
912             final Double initialTransmittedPowerdBm,
913             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
914         this(readings, initialTransmittedPowerdBm, listener);
915         internalSetQualityScores(qualityScores);
916     }
917 
918     /**
919      * Constructor.
920      * Sets signal readings belonging to the same radio source.
921      *
922      * @param qualityScores              quality scores corresponding to each provided
923      *                                   sample. The larger the score value the better
924      *                                   the quality of the sample.
925      * @param readings                   signal readings belonging to the same radio source.
926      * @param initialPosition            initial position to start the estimation of radio
927      *                                   source position.
928      * @param initialTransmittedPowerdBm initial transmitted power to start the
929      *                                   estimation of radio source transmitted power
930      *                                   (expressed in dBm's).
931      * @throws IllegalArgumentException if readings are not valid, quality scores
932      *                                  is null, or length of quality scores is less than required minimum.
933      */
934     protected SequentialRobustMixedRadioSourceEstimator(
935             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings,
936             final P initialPosition, final Double initialTransmittedPowerdBm) {
937         this(readings, initialPosition, initialTransmittedPowerdBm);
938         internalSetQualityScores(qualityScores);
939     }
940 
941     /**
942      * Constructor.
943      *
944      * @param qualityScores              quality scores corresponding to each provided
945      *                                   sample. The larger the score value the better
946      *                                   the quality of the sample.
947      * @param initialPosition            initial position to start the estimation of radio
948      *                                   source position.
949      * @param initialTransmittedPowerdBm initial transmitted power to start the
950      *                                   estimation of radio source transmitted power
951      *                                   (expressed in dBm's).
952      * @throws IllegalArgumentException if quality scores is null, or length
953      *                                  of quality scores is less than required minimum.
954      */
955     protected SequentialRobustMixedRadioSourceEstimator(
956             final double[] qualityScores, final P initialPosition, final Double initialTransmittedPowerdBm) {
957         this(initialPosition, initialTransmittedPowerdBm);
958         internalSetQualityScores(qualityScores);
959     }
960 
961     /**
962      * Constructor.
963      *
964      * @param qualityScores              quality scores corresponding to each provided
965      *                                   sample. The larger the score value the better
966      *                                   the quality of the sample.
967      * @param initialPosition            initial position to start the estimation of radio
968      *                                   source position.
969      * @param initialTransmittedPowerdBm initial transmitted power to start the
970      *                                   estimation of radio source transmitted power
971      *                                   (expressed in dBm's).
972      * @param listener                   in charge of attending events raised by this instance.
973      * @throws IllegalArgumentException if quality scores is null, or length
974      *                                  of quality scores is less than required minimum.
975      */
976     protected SequentialRobustMixedRadioSourceEstimator(
977             final double[] qualityScores, final P initialPosition, final Double initialTransmittedPowerdBm,
978             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
979         this(initialPosition, initialTransmittedPowerdBm, listener);
980         internalSetQualityScores(qualityScores);
981     }
982 
983     /**
984      * Constructor.
985      * Sets signal readings belonging to the same radio source.
986      *
987      * @param qualityScores              quality scores corresponding to each provided
988      *                                   sample. The larger the score value the better
989      *                                   the quality of the sample.
990      * @param readings                   signal readings belonging to the same radio source.
991      * @param initialPosition            initial position to start the estimation of radio
992      *                                   source position.
993      * @param initialTransmittedPowerdBm initial transmitted power to start the
994      *                                   estimation of radio source transmitted power
995      *                                   (expressed in dBm's).
996      * @param listener                   listener in charge of attending events raised by this instance.
997      * @throws IllegalArgumentException if readings are not valid, quality scores
998      *                                  is null, or length of quality scores is less than required minimum.
999      */
1000     protected SequentialRobustMixedRadioSourceEstimator(
1001             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings, final P initialPosition,
1002             final Double initialTransmittedPowerdBm,
1003             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
1004         this(readings, initialPosition, initialTransmittedPowerdBm, listener);
1005         internalSetQualityScores(qualityScores);
1006     }
1007 
1008     /**
1009      * Constructor.
1010      * Sets signal readings belonging to the same radio source.
1011      *
1012      * @param qualityScores              quality scores corresponding to each provided
1013      *                                   sample. The larger the score value the better
1014      *                                   the quality of the sample.
1015      * @param readings                   signal readings belonging to the same radio source.
1016      * @param initialPosition            initial position to start the estimation of radio
1017      *                                   source position.
1018      * @param initialTransmittedPowerdBm initial transmitted power to start the
1019      *                                   estimation of radio source transmitted power
1020      *                                   (expressed in dBm's).
1021      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
1022      * @throws IllegalArgumentException if readings are not valid, quality scores
1023      *                                  is null, or length of quality scores is less than required minimum.
1024      */
1025     protected SequentialRobustMixedRadioSourceEstimator(
1026             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings, final P initialPosition,
1027             final Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
1028         this(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
1029         internalSetQualityScores(qualityScores);
1030     }
1031 
1032     /**
1033      * Constructor.
1034      *
1035      * @param qualityScores              quality scores corresponding to each provided
1036      *                                   sample. The larger the score value the better
1037      *                                   the quality of the sample.
1038      * @param initialPosition            initial position to start the estimation of radio
1039      *                                   source position.
1040      * @param initialTransmittedPowerdBm initial transmitted power to start the
1041      *                                   estimation of radio source transmitted power
1042      *                                   (expressed in dBm's).
1043      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
1044      * @throws IllegalArgumentException if quality scores is null, or length
1045      *                                  of quality scores is less than required minimum.
1046      */
1047     protected SequentialRobustMixedRadioSourceEstimator(
1048             final double[] qualityScores, final P initialPosition, final Double initialTransmittedPowerdBm,
1049             final double initialPathLossExponent) {
1050         this(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
1051         internalSetQualityScores(qualityScores);
1052     }
1053 
1054     /**
1055      * Constructor.
1056      *
1057      * @param qualityScores              quality scores corresponding to each provided
1058      *                                   sample. The larger the score value the better
1059      *                                   the quality of the sample.
1060      * @param initialPosition            initial position to start the estimation of radio
1061      *                                   source position.
1062      * @param initialTransmittedPowerdBm initial transmitted power to start the
1063      *                                   estimation of radio source transmitted power
1064      *                                   (expressed in dBm's).
1065      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
1066      * @param listener                   listener in charge of attending events raised by this instance.
1067      * @throws IllegalArgumentException if quality scores is null, or length
1068      *                                  of quality scores is less than required minimum.
1069      */
1070     protected SequentialRobustMixedRadioSourceEstimator(
1071             final double[] qualityScores, final P initialPosition, final Double initialTransmittedPowerdBm,
1072             final double initialPathLossExponent,
1073             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
1074         this(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
1075         internalSetQualityScores(qualityScores);
1076     }
1077 
1078     /**
1079      * Constructors.
1080      * Sets signal readings belonging to the same radio source.
1081      *
1082      * @param qualityScores              quality scores corresponding to each provided
1083      *                                   sample. The larger the score value the better
1084      *                                   the quality of the sample.
1085      * @param readings                   signal readings belonging to the same radio source.
1086      * @param initialPosition            initial position to start the estimation of radio
1087      *                                   source position.
1088      * @param initialTransmittedPowerdBm initial transmitted power to start the
1089      *                                   estimation of radio source transmitted power
1090      *                                   (expressed in dBm's).
1091      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
1092      * @param listener                   listener in charge of attending events raised by this instance.
1093      * @throws IllegalArgumentException if readings are not valid, quality scores
1094      *                                  is null, or length of quality scores is less than required minimum.
1095      */
1096     protected SequentialRobustMixedRadioSourceEstimator(
1097             final double[] qualityScores, final List<? extends ReadingLocated<P>> readings, final P initialPosition,
1098             final Double initialTransmittedPowerdBm, final double initialPathLossExponent,
1099             final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener) {
1100         this(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
1101         internalSetQualityScores(qualityScores);
1102     }
1103 
1104     /**
1105      * Indicates whether estimator is locked during estimation.
1106      *
1107      * @return true if estimator is locked, false otherwise.
1108      */
1109     public boolean isLocked() {
1110         return locked;
1111     }
1112 
1113     /**
1114      * Returns amount of progress variation before notifying a progress change during
1115      * estimation.
1116      *
1117      * @return amount of progress variation before notifying a progress change during
1118      * estimation.
1119      */
1120     public float getProgressDelta() {
1121         return progressDelta;
1122     }
1123 
1124     /**
1125      * Sets amount of progress variation before notifying a progress change during
1126      * estimation.
1127      *
1128      * @param progressDelta amount of progress variation before notifying a progress
1129      *                      change during estimation.
1130      * @throws IllegalArgumentException if progress delta is less than zero or greater than 1.
1131      * @throws LockedException          if this estimator is locked.
1132      */
1133     public void setProgressDelta(final float progressDelta) throws LockedException {
1134         if (isLocked()) {
1135             throw new LockedException();
1136         }
1137         if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
1138             throw new IllegalArgumentException();
1139         }
1140         this.progressDelta = progressDelta;
1141     }
1142 
1143     /**
1144      * Gets robust method used for robust position estimation using ranging data.
1145      *
1146      * @return robust method used for robust position estimation.
1147      */
1148     public RobustEstimatorMethod getRangingRobustMethod() {
1149         return rangingRobustMethod;
1150     }
1151 
1152     /**
1153      * Sets robust method used for robust position estimation using ranging data.
1154      *
1155      * @param rangingRobustMethod robust method used for robust position estimation.
1156      * @throws LockedException if estimator is locked.
1157      */
1158     public void setRangingRobustMethod(final RobustEstimatorMethod rangingRobustMethod) throws LockedException {
1159         if (isLocked()) {
1160             throw new LockedException();
1161         }
1162         this.rangingRobustMethod = rangingRobustMethod;
1163     }
1164 
1165     /**
1166      * Gets robust method used for path-loss exponent and transmitted power estimation
1167      * using RSSI data.
1168      *
1169      * @return robust method used for path-loss exponent and transmitted power
1170      * estimation.
1171      */
1172     public RobustEstimatorMethod getRssiRobustMethod() {
1173         return rssiRobustMethod;
1174     }
1175 
1176     /**
1177      * Sets robust method used for path-loss exponent and transmitted power estimation
1178      * using RSSI data.
1179      *
1180      * @param rssiRobustMethod robust method used for path-loss exponent and transmitted
1181      *                         power estimation.
1182      * @throws LockedException if estimator is locked.
1183      */
1184     public void setRssiRobustMethod(final RobustEstimatorMethod rssiRobustMethod) throws LockedException {
1185         if (isLocked()) {
1186             throw new LockedException();
1187         }
1188         this.rssiRobustMethod = rssiRobustMethod;
1189     }
1190 
1191     /**
1192      * Gets size of subsets to be checked during ranging robust estimation.
1193      *
1194      * @return size of subsets to be checked during ranging robust estimation.
1195      */
1196     public int getRangingPreliminarySubsetSize() {
1197         return rangingPreliminarySubsetSize;
1198     }
1199 
1200     /**
1201      * Sets size of subsets to be checked during ranging robust estimation.
1202      *
1203      * @param rangingPreliminarySubsetSize size of subsets to be checked during
1204      *                                     ranging robust estimation.
1205      * @throws LockedException          if estimator is locked.
1206      * @throws IllegalArgumentException if provided value is less than {@link #getMinReadings()}.
1207      */
1208     public void setRangingPreliminarySubsetSize(final int rangingPreliminarySubsetSize) throws LockedException {
1209         if (isLocked()) {
1210             throw new LockedException();
1211         }
1212         if (rangingPreliminarySubsetSize < getMinReadings()) {
1213             throw new IllegalArgumentException();
1214         }
1215 
1216         this.rangingPreliminarySubsetSize = rangingPreliminarySubsetSize;
1217     }
1218 
1219     /**
1220      * Gets size of subsets to be checked during RSSI robust estimation.
1221      *
1222      * @return size of subsets to be checked during RSSI robust estimation.
1223      */
1224     public int getRssiPreliminarySubsetSize() {
1225         return rssiPreliminarySubsetSize;
1226     }
1227 
1228     /**
1229      * Sets size of subsets to be checked during RSSI robust estimation.
1230      *
1231      * @param rssiPreliminarySubsetSize size of subsets to be checked during
1232      *                                  RSSI robust estimation.
1233      * @throws LockedException          if estimator is locked.
1234      * @throws IllegalArgumentException if provided value is less than {@link #getMinReadings()}.
1235      */
1236     public void setRssiPreliminarySubsetSize(final int rssiPreliminarySubsetSize) throws LockedException {
1237         if (isLocked()) {
1238             throw new LockedException();
1239         }
1240         if (rssiPreliminarySubsetSize < getMinReadings()) {
1241             throw new IllegalArgumentException();
1242         }
1243 
1244         this.rssiPreliminarySubsetSize = rssiPreliminarySubsetSize;
1245     }
1246 
1247     /**
1248      * Gets threshold to determine when samples are inliers or not, used during robust
1249      * position estimation.
1250      * If not defined, default threshold will be used.
1251      *
1252      * @return threshold for ranging estimation or null.
1253      */
1254     public Double getRangingThreshold() {
1255         return rangingThreshold;
1256     }
1257 
1258     /**
1259      * Sets threshold to determine when samples are inliers or not, used during robust
1260      * position estimation.
1261      * If not defined, default threshold will be used.
1262      *
1263      * @param rangingThreshold threshold for ranging estimation or null.
1264      * @throws LockedException if estimator is locked.
1265      */
1266     public void setRangingThreshold(final Double rangingThreshold) throws LockedException {
1267         if (isLocked()) {
1268             throw new LockedException();
1269         }
1270         this.rangingThreshold = rangingThreshold;
1271     }
1272 
1273     /**
1274      * Gets threshold to determine when samples are inliers or not, used during robust
1275      * path-loss exponent and transmitted power estimation.
1276      * If not defined, default threshold will be used.
1277      *
1278      * @return threshold for RSSI estimation or null.
1279      */
1280     public Double getRssiThreshold() {
1281         return rssiThreshold;
1282     }
1283 
1284     /**
1285      * Sets threshold to determine when samples are inliers or not, used during robust
1286      * path-loss exponent and transmitted power estimation.
1287      * If not defined, default threshold will be used.
1288      *
1289      * @param rssiThreshold threshold for RSSI estimation or null.
1290      * @throws LockedException if estimator is locked.
1291      */
1292     public void setRssiThreshold(final Double rssiThreshold) throws LockedException {
1293         if (isLocked()) {
1294             throw new LockedException();
1295         }
1296         this.rssiThreshold = rssiThreshold;
1297     }
1298 
1299     /**
1300      * Returns amount of confidence expressed as a value between 0.0 and 1.0
1301      * (which is equivalent to 100%) for robust position estimation. The amount of
1302      * confidence indicates the probability that the estimated result is correct.
1303      * Usually this value will be close to 1.0, but not exactly 1.0.
1304      *
1305      * @return amount of confidence for robust position estimation as a value
1306      * between 0.0 and 1.0.
1307      */
1308     public double getRangingConfidence() {
1309         return rangingConfidence;
1310     }
1311 
1312     /**
1313      * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which is
1314      * equivalent to 100%) for robust position estimation. The amount of confidence
1315      * indicates the probability that the estimated result is correct. Usually this
1316      * value will be close to 1.0, but not exactly 1.0.
1317      *
1318      * @param rangingConfidence confidence to be set for robust position estimation
1319      *                          as a value between 0.0 and 1.0.
1320      * @throws IllegalArgumentException if provided value is not between 0.0 and 1.0.
1321      * @throws LockedException          if estimator is locked.
1322      */
1323     public void setRangingConfidence(final double rangingConfidence) throws LockedException {
1324         if (isLocked()) {
1325             throw new LockedException();
1326         }
1327         if (rangingConfidence < MIN_CONFIDENCE || rangingConfidence > MAX_CONFIDENCE) {
1328             throw new IllegalArgumentException();
1329         }
1330         this.rangingConfidence = rangingConfidence;
1331     }
1332 
1333     /**
1334      * Returns amount of confidence expressed as a value between 0.0 and 1.0
1335      * (which is equivalent to 100%) for path-loss exponent and transmitted power
1336      * estimation. The amount of confidence indicates the probability that the
1337      * estimated result is correct.
1338      * Usually this value will be close to 1.0, but not exactly 1.0.
1339      *
1340      * @return amount of confidence for robust path-loss exponent and transmitted power
1341      * estimation as a value between 0.0 and 1.0.
1342      */
1343     public double getRssiConfidence() {
1344         return rssiConfidence;
1345     }
1346 
1347     /**
1348      * Sets amount of confidence expressed as a value between 0.0 and 1.0
1349      * (which is equivalent to 100%) for path-loss exponent and transmitted power
1350      * estimation. The amount of confidence indicates the probability that the
1351      * estimated result is correct. Usually this value will be close to 10.0, but
1352      * not exactly 1.0.
1353      *
1354      * @param rssiConfidence confidence to be set for robust path-loss exponent and
1355      *                       transmitted power estimation as a value between 0.0 and
1356      *                       1.0.
1357      * @throws IllegalArgumentException if provided value is not between 0.0 and 1.0.
1358      * @throws LockedException          if estimator is locked.
1359      */
1360     public void setRssiConfidence(final double rssiConfidence) throws LockedException {
1361         if (isLocked()) {
1362             throw new LockedException();
1363         }
1364         if (rssiConfidence < MIN_CONFIDENCE || rssiConfidence > MAX_CONFIDENCE) {
1365             throw new IllegalArgumentException();
1366         }
1367         this.rssiConfidence = rssiConfidence;
1368     }
1369 
1370     /**
1371      * Returns maximum allowed number of iterations for robust position estimation. If
1372      * maximum allowed number of iterations is achieved without converging to a result
1373      * when calling estimate(), a RobustEstimatorException will be raised.
1374      *
1375      * @return maximum allowed number of iterations for position estimation.
1376      */
1377     public int getRangingMaxIterations() {
1378         return rangingMaxIterations;
1379     }
1380 
1381     /**
1382      * Sets maximum allowed number of iterations for robust position estimation. When
1383      * the maximum number of iterations is exceeded, an approximate result might be
1384      * available for retrieval.
1385      *
1386      * @param rangingMaxIterations maximum allowed number of iterations to be set
1387      *                             for position estimation.
1388      * @throws IllegalArgumentException if provided value is less than 1.
1389      * @throws LockedException          if this estimator is locked.
1390      */
1391     public void setRangingMaxIterations(final int rangingMaxIterations) throws LockedException {
1392         if (isLocked()) {
1393             throw new LockedException();
1394         }
1395         if (rangingMaxIterations < MIN_ITERATIONS) {
1396             throw new IllegalArgumentException();
1397         }
1398         this.rangingMaxIterations = rangingMaxIterations;
1399     }
1400 
1401     /**
1402      * Returns maximum allowed number of iterations for robust path-loss exponent and
1403      * transmitted power estimation. If maximum allowed number of iterations is achieved
1404      * without converging to a result when calling estimate(), a RobustEstimatorException
1405      * will be raised.
1406      *
1407      * @return maximum allowed number of iterations for path-loss exponent and transmitted
1408      * power estimation.
1409      */
1410     public int getRssiMaxIterations() {
1411         return rssiMaxIterations;
1412     }
1413 
1414     /**
1415      * Sets maximum allowed number of iterations for robust path-loss exponent and
1416      * transmitted power estimation. When the maximum number of iterations is exceeded,
1417      * an approximate result might be available for retrieval.
1418      *
1419      * @param rssiMaxIterations maximum allowed number of iterations to be set for
1420      *                          path-loss exponent and transmitted power estimation.
1421      * @throws IllegalArgumentException if provided value is less than 1.
1422      * @throws LockedException          if this estimator is locked.
1423      */
1424     public void setRssiMaxIterations(final int rssiMaxIterations) throws LockedException {
1425         if (isLocked()) {
1426             throw new LockedException();
1427         }
1428         if (rssiMaxIterations < MIN_ITERATIONS) {
1429             throw new IllegalArgumentException();
1430         }
1431         this.rssiMaxIterations = rssiMaxIterations;
1432     }
1433 
1434     /**
1435      * Indicates whether result must be refined using a non-linear solver over found inliers.
1436      *
1437      * @return true to refine result, false to simply use result found by robust estimator
1438      * without further refining.
1439      */
1440     public boolean isResultRefined() {
1441         return refineResult;
1442     }
1443 
1444     /**
1445      * Specifies whether result must be refined using a non-linear solver over found inliers.
1446      *
1447      * @param refineResult true to refine result, false to simply use result found by robust
1448      *                     estimator without further refining.
1449      * @throws LockedException if estimator is locked.
1450      */
1451     public void setResultRefined(final boolean refineResult) throws LockedException {
1452         if (isLocked()) {
1453             throw new LockedException();
1454         }
1455         this.refineResult = refineResult;
1456     }
1457 
1458     /**
1459      * Indicates whether covariance must be kept after refining result.
1460      * This setting is only taken into account if result is refined.
1461      *
1462      * @return true if covariance must be kept after refining result, false otherwise.
1463      */
1464     public boolean isCovarianceKept() {
1465         return keepCovariance;
1466     }
1467 
1468     /**
1469      * Specifies whether covariance must be kept after refining result.
1470      * This setting is only taken into account if result is refined.
1471      *
1472      * @param keepCovariance true if covariance must be kept after refining result,
1473      *                       false otherwise.
1474      * @throws LockedException if estimator is locked.
1475      */
1476     public void setCovarianceKept(final boolean keepCovariance) throws LockedException {
1477         if (isLocked()) {
1478             throw new LockedException();
1479         }
1480         this.keepCovariance = keepCovariance;
1481     }
1482 
1483     /**
1484      * Gets signal readings belonging to the same radio source.
1485      *
1486      * @return signal readings belonging to the same radio source.
1487      */
1488     public List<ReadingLocated<P>> getReadings() {
1489         //noinspection unchecked
1490         return (List<ReadingLocated<P>>) readings;
1491     }
1492 
1493     /**
1494      * Sets signal readings belonging to the same radio source.
1495      *
1496      * @param readings signal readings belonging to the same
1497      *                 radio source.
1498      * @throws LockedException          if estimator is locked.
1499      * @throws IllegalArgumentException if readings are not valid.
1500      */
1501     public void setReadings(final List<? extends ReadingLocated<P>> readings) throws LockedException {
1502         if (isLocked()) {
1503             throw new LockedException();
1504         }
1505 
1506         internalSetReadings(readings);
1507     }
1508 
1509     /**
1510      * Gets listener in charge of attending events raised by this instance.
1511      *
1512      * @return listener in charge of attending events raised by this instance.
1513      */
1514     public SequentialRobustMixedRadioSourceEstimatorListener<S, P> getListener() {
1515         return listener;
1516     }
1517 
1518     /**
1519      * Sets listener in charge of attending events raised by this instance.
1520      *
1521      * @param listener listener in charge of attending events raised by this
1522      *                 instance.
1523      * @throws LockedException if estimator is locked.
1524      */
1525     public void setListener(final SequentialRobustMixedRadioSourceEstimatorListener<S, P> listener)
1526             throws LockedException {
1527         if (isLocked()) {
1528             throw new LockedException();
1529         }
1530 
1531         this.listener = listener;
1532     }
1533 
1534     /**
1535      * Returns quality scores corresponding to each pair of
1536      * positions and distances (i.e. sample).
1537      * The larger the score value the better the quality of the sample.
1538      * This implementation always returns null.
1539      * Subclasses using quality scores must implement proper behavior.
1540      *
1541      * @return quality scores corresponding to each sample.
1542      */
1543     public double[] getQualityScores() {
1544         return qualityScores;
1545     }
1546 
1547     /**
1548      * Sets quality scores corresponding to each pair of positions and
1549      * distances (i.e. sample).
1550      * The larger the score value the better the quality of the sample.
1551      * This implementation makes no action.
1552      * Subclasses using quality scores must implement proper behaviour.
1553      *
1554      * @param qualityScores quality scores corresponding to each pair of
1555      *                      matched points.
1556      * @throws IllegalArgumentException if provided quality scores length
1557      *                                  is smaller than minimum required samples.
1558      * @throws LockedException          if robust solver is locked because an
1559      *                                  estimation is already in progress.
1560      */
1561     public void setQualityScores(final double[] qualityScores) throws LockedException {
1562         if (isLocked()) {
1563             throw new LockedException();
1564         }
1565         internalSetQualityScores(qualityScores);
1566     }
1567 
1568     /**
1569      * Gets initial transmitted power to start the estimation of radio source
1570      * transmitted power (expressed in dBm's).
1571      * If not defined, average value of received power readings will be used.
1572      *
1573      * @return initial transmitted power to start the estimation of radio source
1574      * transmitted power.
1575      */
1576     public Double getInitialTransmittedPowerdBm() {
1577         return initialTransmittedPowerdBm;
1578     }
1579 
1580     /**
1581      * Sets initial transmitted power to start the estimation of radio source
1582      * transmitted power (expressed in dBm's).
1583      * If not defined, average value of received power readings will be used.
1584      *
1585      * @param initialTransmittedPowerdBm initial transmitted power to start the
1586      *                                   estimation of radio source transmitted
1587      *                                   power.
1588      * @throws LockedException if estimator is locked.
1589      */
1590     public void setInitialTransmittedPowerdBm(final Double initialTransmittedPowerdBm) throws LockedException {
1591         if (isLocked()) {
1592             throw new LockedException();
1593         }
1594         this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
1595     }
1596 
1597     /**
1598      * Gets initial transmitted power to start the estimation of radio source
1599      * transmitted power (expressed in mW).
1600      * If not defined, average value of received power readings will be used.
1601      *
1602      * @return initial transmitted power to start the estimation of radio source
1603      * transmitted power.
1604      */
1605     public Double getInitialTransmittedPower() {
1606         return initialTransmittedPowerdBm != null ? Utils.dBmToPower(initialTransmittedPowerdBm) : null;
1607     }
1608 
1609     /**
1610      * Sets initial transmitted power to start the estimation of radio source
1611      * transmitted power (expressed in mW).
1612      * If not defined, average value of received power readings will be used.
1613      *
1614      * @param initialTransmittedPower initial transmitted power to start the
1615      *                                estimation of radio source transmitted power.
1616      * @throws LockedException          if estimator is locked.
1617      * @throws IllegalArgumentException if provided value is negative.
1618      */
1619     public void setInitialTransmittedPower(final Double initialTransmittedPower) throws LockedException {
1620         if (isLocked()) {
1621             throw new LockedException();
1622         }
1623         if (initialTransmittedPower != null) {
1624             if (initialTransmittedPower < 0.0) {
1625                 throw new IllegalArgumentException();
1626             }
1627             initialTransmittedPowerdBm = Utils.powerTodBm(initialTransmittedPower);
1628         } else {
1629             initialTransmittedPowerdBm = null;
1630         }
1631     }
1632 
1633     /**
1634      * Gets initial position to start the estimation of radio source position.
1635      * If not defined, centroid of provided fingerprints will be used.
1636      *
1637      * @return initial position to start the estimation of radio source position.
1638      */
1639     public P getInitialPosition() {
1640         return initialPosition;
1641     }
1642 
1643     /**
1644      * Sets initial position to start the estimation of radio source position.
1645      * If not defined, centroid of provided fingerprints will be used.
1646      *
1647      * @param initialPosition initial position to start the estimation of radio
1648      *                        source position.
1649      * @throws LockedException if estimator is locked.
1650      */
1651     public void setInitialPosition(final P initialPosition) throws LockedException {
1652         if (isLocked()) {
1653             throw new LockedException();
1654         }
1655         this.initialPosition = initialPosition;
1656     }
1657 
1658     /**
1659      * Gets initial exponent typically used on free space for path loss propagation
1660      * in terms of distance.
1661      * On different environments path loss exponent might have different value:
1662      * - Free space: 2.0
1663      * - Urban Area: 2.7 to 3.5
1664      * - Suburban Area: 3 to 5
1665      * - Indoor (line-of-sight): 1.6 to 1.8
1666      * <p>
1667      * If path loss exponent estimation is enabled, estimation will start at this
1668      * value and will converge to the most appropriate value.
1669      * If path loss exponent estimation is disabled, this value will be assumed
1670      * to be exact and the estimated path loss exponent will be equal to this
1671      * value.
1672      *
1673      * @return initial path loss exponent.
1674      */
1675     public double getInitialPathLossExponent() {
1676         return initialPathLossExponent;
1677     }
1678 
1679     /**
1680      * Sets initial exponent typically used on free space for path loss propagation
1681      * in terms of distance.
1682      * On different environments path loss exponent might have different value:
1683      * - Free space: 2.0
1684      * - Urban Area: 2.7 to 3.5
1685      * - Suburban Area: 3 to 5
1686      * - Indoor (line-of-sight): 1.6 to 1.8
1687      * <p>
1688      * If path loss exponent estimation is enabled, estimation will start at this
1689      * value and will converge to the most appropriate value.
1690      * If path loss exponent estimation is disabled, this value will be assumed
1691      * to be exact and the estimated path loss exponent will be equal to this
1692      * value.
1693      *
1694      * @param initialPathLossExponent initial path loss exponent.
1695      * @throws LockedException if estimator is locked.
1696      */
1697     public void setInitialPathLossExponent(final double initialPathLossExponent) throws LockedException {
1698         if (isLocked()) {
1699             throw new LockedException();
1700         }
1701         this.initialPathLossExponent = initialPathLossExponent;
1702     }
1703 
1704     /**
1705      * Indicates whether transmitted power estimation is enabled or not.
1706      *
1707      * @return true if transmitted power estimation is enabled, false otherwise.
1708      */
1709     public boolean isTransmittedPowerEstimationEnabled() {
1710         return transmittedPowerEstimationEnabled;
1711     }
1712 
1713     /**
1714      * Specifies whether transmitted power estimation is enabled or not.
1715      *
1716      * @param transmittedPowerEstimationEnabled true if transmitted power estimation is enabled,
1717      *                                          false otherwise.
1718      * @throws LockedException if estimator is locked.
1719      */
1720     public void setTransmittedPowerEstimationEnabled(final boolean transmittedPowerEstimationEnabled)
1721             throws LockedException {
1722         if (isLocked()) {
1723             throw new LockedException();
1724         }
1725         this.transmittedPowerEstimationEnabled = transmittedPowerEstimationEnabled;
1726     }
1727 
1728     /**
1729      * Indicates whether path loss estimation is enabled or not.
1730      *
1731      * @return true if path loss estimation is enabled, false otherwise.
1732      */
1733     public boolean isPathLossEstimationEnabled() {
1734         return pathLossEstimationEnabled;
1735     }
1736 
1737     /**
1738      * Specifies whether path loss estimation is enabled or not.
1739      *
1740      * @param pathLossEstimationEnabled true if path loss estimation is enabled,
1741      *                                  false otherwise.
1742      * @throws LockedException if estimator is locked.
1743      */
1744     public void setPathLossEstimationEnabled(final boolean pathLossEstimationEnabled) throws LockedException {
1745         if (isLocked()) {
1746             throw new LockedException();
1747         }
1748         this.pathLossEstimationEnabled = pathLossEstimationEnabled;
1749     }
1750 
1751     /**
1752      * Indicates whether position covariances of readings must be taken into account to increase
1753      * the amount of standard deviation of each ranging measure by the amount of position standard
1754      * deviation assuming that both measures are statistically independent.
1755      *
1756      * @return true to take into account reading position covariances, false otherwise.
1757      */
1758     public boolean getUseReadingPositionCovariance() {
1759         return useReadingPositionCovariances;
1760     }
1761 
1762     /**
1763      * Specifies whether position covariances of readings must be taken into account to increase
1764      * the amount of standard deviation of each ranging measure by the amount of position standard
1765      * deviation assuming that both measures are statistically independent.
1766      *
1767      * @param useReadingPositionCovariances true to take into account reading position covariances, false
1768      *                                      otherwise.
1769      * @throws LockedException if estimator is locked.
1770      */
1771     public void setUseReadingPositionCovariances(final boolean useReadingPositionCovariances) throws LockedException {
1772         if (isLocked()) {
1773             throw new LockedException();
1774         }
1775         this.useReadingPositionCovariances = useReadingPositionCovariances;
1776     }
1777 
1778     /**
1779      * Indicates whether an homogeneous ranging linear solver is used to estimate preliminary
1780      * positions.
1781      *
1782      * @return true if homogeneous ranging linear solver is used, false if an inhomogeneous ranging linear
1783      * one is used instead.
1784      */
1785     public boolean isHomogeneousRangingLinearSolverUsed() {
1786         return useHomogeneousRangingLinearSolver;
1787     }
1788 
1789     /**
1790      * Specifies whether an homogeneous ranging linear solver is used to estimate preliminary
1791      * positions.
1792      *
1793      * @param useHomogeneousRangingLinearSolver true if homogeneous ranging linear solver is used, false
1794      *                                          if an inhomogeneous ranging linear one is used instead.
1795      * @throws LockedException if estimator is locked.
1796      */
1797     public void setHomogeneousRangingLinearSolverUsed(final boolean useHomogeneousRangingLinearSolver)
1798             throws LockedException {
1799         if (isLocked()) {
1800             throw new LockedException();
1801         }
1802 
1803         this.useHomogeneousRangingLinearSolver = useHomogeneousRangingLinearSolver;
1804     }
1805 
1806 
1807     /**
1808      * Gets covariance for estimated position and power.
1809      * Matrix contains information in the following order:
1810      * Top-left sub-matrix contains covariance of position,
1811      * then follows transmitted power variance, and finally
1812      * the last element contains path-loss exponent variance.
1813      * This is only available when result has been refined and covariance is kept.
1814      *
1815      * @return covariance for estimated position and power.
1816      */
1817     public Matrix getCovariance() {
1818         return covariance;
1819     }
1820 
1821     /**
1822      * Gets estimated position covariance.
1823      * Size of this matrix will depend on the number of dimensions
1824      * of estimated position (either 2 or 3).
1825      * This is only available when result has been refined and covariance is kept.
1826      *
1827      * @return estimated position covariance.
1828      */
1829     public Matrix getEstimatedPositionCovariance() {
1830         return estimatedPositionCovariance;
1831     }
1832 
1833     /**
1834      * Gets estimated position.
1835      *
1836      * @return estimated position.
1837      */
1838     public P getEstimatedPosition() {
1839         return estimatedPosition;
1840     }
1841 
1842     /**
1843      * Indicates whether readings are valid or not.
1844      * Readings are considered valid when there are enough readings.
1845      *
1846      * @param readings readings to be validated.
1847      * @return true if readings are valid, false otherwise.
1848      */
1849     public boolean areValidReadings(final List<? extends ReadingLocated<P>> readings) {
1850         if (readings == null) {
1851             return false;
1852         }
1853 
1854         checkReadings(readings);
1855 
1856         // if enough ranging data is available, we check validity both for ranging and RSSI readings
1857         return ((!rssiPositionEnabled && numRangingReadings >= getMinRangingReadings()
1858                 && numRssiReadings >= getMinRssiReadings())
1859                 // if not enough ranging data is available, we check validity only for RSSI readings
1860                 || (rssiPositionEnabled && numRssiReadings >= getMinRssiReadings())
1861                 // if only position is enabled, then only check for ranging readings
1862                 || (!transmittedPowerEstimationEnabled && !pathLossEstimationEnabled
1863                 && numRangingReadings >= getMinRangingReadings()))
1864                 // in both upper cases enough general readings must be available
1865                 && readings.size() >= getMinReadings();
1866     }
1867 
1868     /**
1869      * Indicates whether this instance is ready to start the estimation.
1870      *
1871      * @return true if this instance is ready, false otherwise.
1872      * @throws LockedException if estimator is locked
1873      */
1874     public boolean isReady() throws LockedException {
1875         checkReadings(readings);
1876 
1877         buildRangingEstimatorIfNeeded();
1878         setupRangingEstimator();
1879 
1880         if (transmittedPowerEstimationEnabled || pathLossEstimationEnabled) {
1881             buildRssiEstimatorIfNeeded();
1882             setupRssiEstimator();
1883         }
1884 
1885         if (rssiPositionEnabled) {
1886             return rssiEstimator.isReady();
1887         } else {
1888             return rangingEstimator.isReady() && ((!transmittedPowerEstimationEnabled && !pathLossEstimationEnabled)
1889                     || rssiEstimator.isReady());
1890         }
1891     }
1892 
1893     /**
1894      * Gets minimum required number of ranging or ranging+rssi readings
1895      * required to start estimation.
1896      *
1897      * @return minimum required number of ranging or ranging+rssi readings.
1898      */
1899     public int getMinRangingReadings() {
1900         return getNumberOfDimensions() + 1;
1901     }
1902 
1903     /**
1904      * Gets minimum required number of rssi or ranging+rssi readings
1905      * required to start estimation.
1906      *
1907      * @return minimum required number of rssi or ranging+rssi readings.
1908      */
1909     public int getMinRssiReadings() {
1910         return getMinReadings();
1911     }
1912 
1913     /**
1914      * Gets minimum required number of readings to estimate
1915      * power, position and path-loss exponent.
1916      * This value depends on the number of parameters to
1917      * be estimated, but for position only, this is 3
1918      * readings for 2D, and 4 readings for 3D.
1919      *
1920      * @return minimum required number of readings.
1921      */
1922     public abstract int getMinReadings();
1923 
1924     /**
1925      * Gets number of dimensions of position points.
1926      *
1927      * @return number of dimensions of position points.
1928      */
1929     public abstract int getNumberOfDimensions();
1930 
1931     /**
1932      * Gets estimated transmitted power variance.
1933      * This is only available when result has been refined and covariance is kept.
1934      *
1935      * @return estimated transmitted power variance.
1936      */
1937     public Double getEstimatedTransmittedPowerVariance() {
1938         return estimatedTransmittedPowerVariance;
1939     }
1940 
1941     /**
1942      * Gets estimated path loss exponent variance.
1943      * This is only available when result has been refined and covariance is kept.
1944      *
1945      * @return estimated path loss exponent variance.
1946      */
1947     public Double getEstimatedPathLossExponentVariance() {
1948         return estimatedPathLossExponentVariance;
1949     }
1950 
1951     /**
1952      * Gets estimated transmitted power expressed in milli watts (mW) or null if
1953      * not available.
1954      *
1955      * @return estimated transmitted power expressed in milli watts or null.
1956      */
1957     public Double getEstimatedTransmittedPower() {
1958         return estimatedTransmittedPowerdBm != null ? Utils.dBmToPower(estimatedTransmittedPowerdBm) : null;
1959     }
1960 
1961     /**
1962      * Gets estimated transmitted power expressed in dBm's or null if not available.
1963      *
1964      * @return estimated transmitted power expressed in dBm's or null.
1965      */
1966     public Double getEstimatedTransmittedPowerdBm() {
1967         return estimatedTransmittedPowerdBm;
1968     }
1969 
1970     /**
1971      * Gets estimated exponent typically used on free space for path loss propagation in
1972      * terms of distance.
1973      * On different environments path loss exponent might have different values:
1974      * - Free space: 2.0
1975      * - Urban Area: 2.7 to 3.5
1976      * - Suburban Area: 3 to 5
1977      * - Indoor (line-of-sight): 1.6 to 1.8
1978      * If path loss exponent estimation is not enabled, this value will always be equal to
1979      * {@link RssiRadioSourceEstimator#DEFAULT_PATH_LOSS_EXPONENT}
1980      *
1981      * @return estimated path loss exponent.
1982      */
1983     public double getEstimatedPathLossExponent() {
1984         return estimatedPathLossExponent;
1985     }
1986 
1987     /**
1988      * Robustly estimates position, transmitted power and path-loss exponent for a
1989      * radio source.
1990      *
1991      * @throws LockedException          if instance is busy during estimation.
1992      * @throws NotReadyException        if estimator is not ready.
1993      * @throws RobustEstimatorException if estimation fails for any reason
1994      *                                  (i.e. numerical instability, no solution available, etc).
1995      */
1996     public void estimate() throws LockedException, NotReadyException, RobustEstimatorException {
1997         if (isLocked()) {
1998             throw new LockedException();
1999         }
2000         try {
2001             locked = true;
2002 
2003             // when checking for readiness, inner estimators are created and setup
2004             if (!isReady()) {
2005                 throw new NotReadyException();
2006             }
2007 
2008             if (listener != null) {
2009                 listener.onEstimateStart(this);
2010             }
2011 
2012             // estimate position
2013             if (!rssiPositionEnabled) {
2014                 rangingEstimator.setPreliminarySubsetSize(rangingPreliminarySubsetSize);
2015 
2016                 rangingEstimator.estimate();
2017 
2018                 estimatedPosition = rangingEstimator.getEstimatedPosition();
2019                 estimatedPositionCovariance = rangingEstimator.getEstimatedPositionCovariance();
2020                 inliersData = rangingEstimator.getInliersData();
2021             } else {
2022                 estimatedPosition = null;
2023             }
2024 
2025             // estimate transmitted power and/or path-loss if enabled
2026             if (transmittedPowerEstimationEnabled || pathLossEstimationEnabled || rssiPositionEnabled) {
2027                 rssiEstimator.setPositionEstimationEnabled(rssiPositionEnabled);
2028                 rssiEstimator.setInitialPosition(estimatedPosition);
2029                 rssiEstimator.setPreliminarySubsetSize(rssiPreliminarySubsetSize);
2030 
2031                 rssiEstimator.estimate();
2032 
2033                 if (rssiPositionEnabled) {
2034                     estimatedPosition = rssiEstimator.getEstimatedPosition();
2035                     estimatedPositionCovariance = rssiEstimator.getEstimatedPositionCovariance();
2036                 }
2037 
2038                 inliersData = rssiEstimator.getInliersData();
2039 
2040                 if (transmittedPowerEstimationEnabled) {
2041                     // transmitted power estimation enabled
2042                     estimatedTransmittedPowerdBm = rssiEstimator.getEstimatedTransmittedPowerdBm();
2043                     estimatedTransmittedPowerVariance = rssiEstimator.getEstimatedTransmittedPowerVariance();
2044                 } else {
2045                     // transmitted power estimation disabled
2046                     estimatedTransmittedPowerdBm = initialTransmittedPowerdBm;
2047                     estimatedTransmittedPowerVariance = null;
2048                 }
2049 
2050                 if (pathLossEstimationEnabled) {
2051                     // path-loss exponent estimation enabled
2052                     estimatedPathLossExponent = rssiEstimator.getEstimatedPathLossExponent();
2053                     estimatedPathLossExponentVariance = rssiEstimator.getEstimatedPathLossExponentVariance();
2054                 } else {
2055                     // path-loss exponent estimation disabled
2056                     estimatedPathLossExponent = initialPathLossExponent;
2057                     estimatedPathLossExponentVariance = null;
2058                 }
2059 
2060                 // build covariance matrix
2061                 if (rssiPositionEnabled) {
2062                     // if only RSSI estimation is done, we use directly the available estimated covariance
2063                     covariance = rssiEstimator.getCovariance();
2064                 } else {
2065                     // if both ranging and RSSI data is used, we build covariance matrix by setting
2066                     // position covariance estimated by ranging estimator into top-left corner, and then
2067                     // adding covariance terms related to path loss exponent and transmitted power
2068                     final var rssiCov = rssiEstimator.getCovariance();
2069                     if (estimatedPositionCovariance != null && rssiCov != null) {
2070                         final var dims = getNumberOfDimensions();
2071                         var n = dims;
2072                         if (transmittedPowerEstimationEnabled) {
2073                             n++;
2074                         }
2075                         if (pathLossEstimationEnabled) {
2076                             n++;
2077                         }
2078 
2079                         final var dimsMinus1 = dims - 1;
2080                         final var nMinus1 = n - 1;
2081                         covariance = new Matrix(n, n);
2082                         covariance.setSubmatrix(0, 0, dimsMinus1, dimsMinus1,
2083                                 estimatedPositionCovariance);
2084                         covariance.setSubmatrix(dims, dims, nMinus1, nMinus1, rssiCov);
2085                     } else {
2086                         covariance = null;
2087                     }
2088                 }
2089             } else {
2090                 covariance = estimatedPositionCovariance;
2091                 estimatedTransmittedPowerdBm = initialTransmittedPowerdBm;
2092                 estimatedTransmittedPowerVariance = null;
2093 
2094                 estimatedPathLossExponent = initialPathLossExponent;
2095                 estimatedPathLossExponentVariance = null;
2096             }
2097 
2098             if (listener != null) {
2099                 listener.onEstimateEnd(this);
2100             }
2101         } catch (final AlgebraException e) {
2102             throw new RobustEstimatorException(e);
2103         } finally {
2104             locked = false;
2105         }
2106     }
2107 
2108     /**
2109      * Gets data related to inliers found after estimation.
2110      *
2111      * @return data related to inliers found after estimation.
2112      */
2113     public InliersData getInliersData() {
2114         return inliersData;
2115     }
2116 
2117     /**
2118      * Indicates whether position is estimated using RSSI data.
2119      * If enough ranging readings are available, this is false and position is estimated using ranging readings,
2120      * otherwise this is true and position is estimated using RSSI data in a less reliable way.
2121      *
2122      * @return true if position is estimated using RSSI data, false if position is estimated using ranging data.
2123      */
2124     public boolean isRssiPositionEnabled() {
2125         return rssiPositionEnabled;
2126     }
2127 
2128     /**
2129      * Gets estimated located radio source.
2130      *
2131      * @param <S2> type of located radio source.
2132      * @return estimated located radio source.
2133      */
2134     public abstract <S2 extends RadioSourceLocated<P>> S2 getEstimatedRadioSource();
2135 
2136     /**
2137      * Builds ranging estimator.
2138      */
2139     protected abstract void buildRangingEstimatorIfNeeded();
2140 
2141     /**
2142      * Build RSSI estimator.
2143      *
2144      * @throws LockedException if estimator is locked.
2145      */
2146     protected abstract void buildRssiEstimatorIfNeeded() throws LockedException;
2147 
2148     /**
2149      * Setups ranging estimator.
2150      *
2151      * @throws LockedException if estimator is locked.
2152      */
2153     protected void setupRangingEstimator() throws LockedException {
2154         if (readings != null && !rssiPositionEnabled) {
2155             // build ranging readings
2156             final var rangingReadings = new ArrayList<RangingReadingLocated<S, P>>();
2157             for (final var reading : readings) {
2158                 if (reading instanceof RangingReadingLocated) {
2159                     rangingReadings.add((RangingReadingLocated<S, P>) reading);
2160                 } else if (reading instanceof RangingAndRssiReadingLocated) {
2161                     rangingReadings.add(createRangingReading((RangingAndRssiReadingLocated<S, P>) reading));
2162                 }
2163             }
2164             rangingEstimator.setReadings(rangingReadings);
2165 
2166             if (qualityScores != null && !rangingReadings.isEmpty()) {
2167                 // build quality scores
2168                 final var numReadings = readings.size();
2169                 final var newNumRangingReadings = rangingReadings.size();
2170                 final var rangingQualityScores = new double[newNumRangingReadings];
2171                 var pos = 0;
2172                 for (var i = 0; i < numReadings; i++) {
2173                     final var reading = readings.get(i);
2174                     if (reading instanceof RangingReadingLocated || reading instanceof RangingAndRssiReadingLocated) {
2175                         rangingQualityScores[pos] = qualityScores[i];
2176                         pos++;
2177                     }
2178                 }
2179 
2180                 rangingEstimator.setQualityScores(rangingQualityScores);
2181             }
2182 
2183             // enable RSSI position estimation only if not enough ranging readings are
2184             // available
2185             rssiPositionEnabled = rangingReadings.size() < rangingEstimator.getMinReadings();
2186         }
2187 
2188         rangingEstimator.setProgressDelta(2.0f * progressDelta);
2189         rangingEstimator.setConfidence(rangingConfidence);
2190         rangingEstimator.setMaxIterations(rangingMaxIterations);
2191         rangingEstimator.setResultRefined(refineResult);
2192         rangingEstimator.setCovarianceKept(keepCovariance);
2193         rangingEstimator.setUseReadingPositionCovariances(useReadingPositionCovariances);
2194         rangingEstimator.setHomogeneousLinearSolverUsed(useHomogeneousRangingLinearSolver);
2195 
2196         rangingEstimator.setInitialPosition(initialPosition);
2197 
2198         rangingEstimator.setListener(new RobustRangingRadioSourceEstimatorListener<>() {
2199             @Override
2200             public void onEstimateStart(final RobustRangingRadioSourceEstimator<S, P> estimator) {
2201                 // not used
2202             }
2203 
2204             @Override
2205             public void onEstimateEnd(final RobustRangingRadioSourceEstimator<S, P> estimator) {
2206                 // not used
2207             }
2208 
2209             @Override
2210             public void onEstimateNextIteration(
2211                     final RobustRangingRadioSourceEstimator<S, P> estimator, final int iteration) {
2212                 // not used
2213             }
2214 
2215             @Override
2216             public void onEstimateProgressChange(
2217                     final RobustRangingRadioSourceEstimator<S, P> estimator, final float progress) {
2218                 if (listener != null) {
2219                     listener.onEstimateProgressChange(
2220                             SequentialRobustMixedRadioSourceEstimator.this, 0.5f * progress);
2221                 }
2222             }
2223         });
2224     }
2225 
2226     /**
2227      * Setups RSSI estimator.
2228      *
2229      * @throws LockedException if estimator is locked.
2230      */
2231     protected void setupRssiEstimator() throws LockedException {
2232         if (readings != null) {
2233             rssiEstimator.setPositionEstimationEnabled(rssiPositionEnabled);
2234 
2235             // build RSSI readings
2236             final var rssiReadings = new ArrayList<RssiReadingLocated<S, P>>();
2237             for (final var reading : readings) {
2238                 if (reading instanceof RssiReadingLocated) {
2239                     rssiReadings.add((RssiReadingLocated<S, P>) reading);
2240                 } else if (reading instanceof RangingAndRssiReadingLocated) {
2241                     rssiReadings.add(createRssiReading((RangingAndRssiReadingLocated<S, P>) reading));
2242                 }
2243             }
2244             rssiEstimator.setReadings(rssiReadings);
2245 
2246             if (qualityScores != null && !rssiReadings.isEmpty()) {
2247                 // build quality scores
2248                 final var numReadings = readings.size();
2249                 final var newNumRssiReadings = rssiReadings.size();
2250                 final var rssiQualityScores = new double[newNumRssiReadings];
2251                 var pos = 0;
2252                 for (var i = 0; i < numReadings; i++) {
2253                     final var reading = readings.get(i);
2254                     if (reading instanceof RssiReadingLocated || reading instanceof RangingAndRssiReadingLocated) {
2255                         rssiQualityScores[pos] = qualityScores[i];
2256                         pos++;
2257                     }
2258                 }
2259 
2260                 rssiEstimator.setQualityScores(rssiQualityScores);
2261             }
2262         }
2263 
2264         rssiEstimator.setProgressDelta(2.0f * progressDelta);
2265         rssiEstimator.setConfidence(rssiConfidence);
2266         rssiEstimator.setMaxIterations(rssiMaxIterations);
2267         rssiEstimator.setResultRefined(refineResult);
2268         rssiEstimator.setCovarianceKept(keepCovariance);
2269 
2270         // initial position is not set because position estimated from ranging measures
2271         // will be later used
2272         rssiEstimator.setInitialTransmittedPowerdBm(initialTransmittedPowerdBm);
2273         rssiEstimator.setInitialPathLossExponent(initialPathLossExponent);
2274 
2275         rssiEstimator.setTransmittedPowerEstimationEnabled(transmittedPowerEstimationEnabled);
2276         rssiEstimator.setPathLossEstimationEnabled(pathLossEstimationEnabled);
2277 
2278         rssiEstimator.setListener(new RobustRssiRadioSourceEstimatorListener<>() {
2279             @Override
2280             public void onEstimateStart(final RobustRssiRadioSourceEstimator<S, P> estimator) {
2281                 // not used
2282             }
2283 
2284             @Override
2285             public void onEstimateEnd(final RobustRssiRadioSourceEstimator<S, P> estimator) {
2286                 // not used
2287             }
2288 
2289             @Override
2290             public void onEstimateNextIteration(
2291                     final RobustRssiRadioSourceEstimator<S, P> estimator, final int iteration) {
2292                 // not used
2293             }
2294 
2295             @Override
2296             public void onEstimateProgressChange(
2297                     final RobustRssiRadioSourceEstimator<S, P> estimator, final float progress) {
2298                 if (listener != null) {
2299                     listener.onEstimateProgressChange(
2300                             SequentialRobustMixedRadioSourceEstimator.this, 0.5f + 0.5f * progress);
2301                 }
2302             }
2303         });
2304     }
2305 
2306     /**
2307      * Internally sets signal readings belonging to the same radio source.
2308      *
2309      * @param readings signal readings belonging to the same radio source.
2310      * @throws IllegalArgumentException if readings are null, not enough readings
2311      *                                  are available, or readings do not belong to the same access point.
2312      */
2313     private void internalSetReadings(final List<? extends ReadingLocated<P>> readings) {
2314         if (!areValidReadings(readings)) {
2315             throw new IllegalArgumentException();
2316         }
2317 
2318         this.readings = readings;
2319     }
2320 
2321     /**
2322      * Sets quality scores corresponding to each provided sample.
2323      * This method is used internally and does not check whether instance is
2324      * locked or not.
2325      *
2326      * @param qualityScores quality scores to be set.
2327      * @throws IllegalArgumentException if provided quality scores length
2328      *                                  is smaller than required minimum.
2329      */
2330     private void internalSetQualityScores(final double[] qualityScores) {
2331         if (qualityScores == null || qualityScores.length < getMinReadings()) {
2332             throw new IllegalArgumentException();
2333         }
2334 
2335         this.qualityScores = qualityScores;
2336     }
2337 
2338     /**
2339      * Creates a ranging reading from a ranging and RSSI reading.
2340      *
2341      * @param reading input reading to convert from.
2342      * @return a ranging reading containing only the ranging data of input reading.
2343      */
2344     private RangingReadingLocated<S, P> createRangingReading(final RangingAndRssiReadingLocated<S, P> reading) {
2345         return new RangingReadingLocated<>(reading.getSource(), reading.getDistance(), reading.getPosition(),
2346                 reading.getDistanceStandardDeviation(), reading.getPositionCovariance());
2347     }
2348 
2349     /**
2350      * Creates an RSSI reading from a ranging and RSSI reading.
2351      *
2352      * @param reading input reading to convert from.
2353      * @return an RSSI reading containing only the RSSI data of input reading.
2354      */
2355     private RssiReadingLocated<S, P> createRssiReading(final RangingAndRssiReadingLocated<S, P> reading) {
2356         return new RssiReadingLocated<>(reading.getSource(), reading.getRssi(), reading.getPosition(),
2357                 reading.getRssiStandardDeviation(), reading.getPositionCovariance());
2358     }
2359 
2360     /**
2361      * Checks number of available ranging readings and number of available RSSI readings. Also determines
2362      * whether position must be estimated using ranging data or RSSI data.
2363      *
2364      * @param readings readings to be checked.
2365      */
2366     private void checkReadings(final List<? extends ReadingLocated<P>> readings) {
2367         numRangingReadings = numRssiReadings = 0;
2368 
2369         if (readings == null) {
2370             return;
2371         }
2372 
2373         for (final var reading : readings) {
2374             if (reading instanceof RangingReadingLocated) {
2375                 numRangingReadings++;
2376 
2377             } else if (reading instanceof RssiReadingLocated) {
2378                 numRssiReadings++;
2379 
2380             } else if (reading instanceof RangingAndRssiReadingLocated) {
2381                 numRangingReadings++;
2382                 numRssiReadings++;
2383             }
2384         }
2385 
2386         rssiPositionEnabled = numRangingReadings < getMinRangingReadings();
2387     }
2388 }