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