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.geometry.Point2D;
19  import com.irurueta.navigation.LockedException;
20  import com.irurueta.navigation.NotReadyException;
21  import com.irurueta.navigation.indoor.RadioSource;
22  import com.irurueta.navigation.indoor.RssiReadingLocated;
23  import com.irurueta.numerical.robust.PROMedSRobustEstimator;
24  import com.irurueta.numerical.robust.PROMedSRobustEstimatorListener;
25  import com.irurueta.numerical.robust.RobustEstimator;
26  import com.irurueta.numerical.robust.RobustEstimatorException;
27  import com.irurueta.numerical.robust.RobustEstimatorMethod;
28  
29  import java.util.List;
30  
31  /**
32   * Robustly estimate 2D position, transmitted power and path-loss exponent of a radio source
33   * (e.g. Wi-Fi access point or bluetooth beacon), by discarding outliers using PROMedS
34   * algorithm and assuming that the radio source emits isotropically following the
35   * expression below:
36   * Pr = Pt*Gt*Gr*lambda^2 / (4*pi*d)^2,
37   * where Pr is the received power (expressed in mW),
38   * Gt is the Gain of the transmission antenna
39   * Gr is the Gain of the receiver antenna
40   * d is the distance between emitter and receiver
41   * and lambda is the wavelength and is equal to: lambda = c / f,
42   * where c is the speed of light
43   * and f is the carrier frequency of the radio signal.
44   * Because usually information about the antenna of the radio source cannot be
45   * retrieved (because many measurements are made on unknown devices where
46   * physical access is not possible), this implementation will estimate the
47   * equivalent transmitted power as: Pte = Pt * Gt * Gr.
48   * If RssiReadings contain RSSI standard deviations, those values will be used,
49   * otherwise it will be assumed an RSSI standard deviation of 1 dB.
50   * Implementations of this class should be able to detect and discard outliers in
51   * order to find the best solution.
52   * <p>
53   * IMPORTANT: When using this class estimation can be done using a
54   * combination of radio source position, transmitted power and path loss
55   * exponent. However enabling all three estimations usually achieves
56   * inaccurate results. When using this class, estimation must be of at least
57   * one parameter (position, transmitted power or path loss exponent) when
58   * initial values are provided for the other two, and at most it should consist
59   * of two parameters (either position and transmitted power, position and
60   * path loss exponent or transmitted power and path loss exponent), providing an
61   * initial value for the remaining parameter.
62   *
63   * @param <S> a {@link RadioSource} type.
64   */
65  @SuppressWarnings("Duplicates")
66  public class PROMedSRobustRssiRadioSourceEstimator2D<S extends RadioSource> extends
67          RobustRssiRadioSourceEstimator2D<S> {
68  
69      /**
70       * Default value to be used for stop threshold. Stop threshold can be used to
71       * avoid keeping the algorithm unnecessarily iterating in case that best
72       * estimated threshold using median of residuals is not small enough. Once a
73       * solution is found that generates a threshold below this value, the
74       * algorithm will stop.
75       * The stop threshold can be used to prevent the LMedS algorithm iterating
76       * too many times in cases where samples have a very similar accuracy.
77       * For instance, in cases where proportion of outliers is very small (close
78       * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
79       * iterate for a long time trying to find the best solution when indeed
80       * there is no need to do that if a reasonable threshold has already been
81       * reached.
82       * Because of this behaviour the stop threshold can be set to a value much
83       * lower than the one typically used in RANSAC, and yet the algorithm could
84       * still produce even smaller thresholds in estimated results.
85       */
86      public static final double DEFAULT_STOP_THRESHOLD = 1e-4;
87  
88      /**
89       * Minimum allowed stop threshold value.
90       */
91      public static final double MIN_STOP_THRESHOLD = 0.0;
92  
93      /**
94       * Threshold to be used to keep the algorithm iterating in case that best
95       * estimated threshold using median of residuals is not small enough. Once
96       * a solution is found that generates a threshold below this value, the
97       * algorithm will stop.
98       * The stop threshold can be used to prevent the LMedS algorithm iterating
99       * too many times in cases where samples have a very similar accuracy.
100      * For instance, in cases where proportion of outliers is very small (close
101      * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
102      * iterate for a long time trying to find the best solution when indeed
103      * there is no need to do that if a reasonable threshold has already been
104      * reached.
105      * Because of this behaviour the stop threshold can be set to a value much
106      * lower than the one typically used in RANSAC, and yet the algorithm could
107      * still produce even smaller thresholds in estimated results.
108      */
109     private double stopThreshold = DEFAULT_STOP_THRESHOLD;
110 
111     /**
112      * Quality scores corresponding to each provided sample.
113      * The larger the score value the better the quality of the sample.
114      */
115     private double[] qualityScores;
116 
117     /**
118      * Constructor.
119      */
120     public PROMedSRobustRssiRadioSourceEstimator2D() {
121         super();
122     }
123 
124     /**
125      * Constructor.
126      * Sets signal readings belonging to the same radio source.
127      *
128      * @param readings signal readings belonging to the same radio source.
129      * @throws IllegalArgumentException if readings are not valid.
130      */
131     public PROMedSRobustRssiRadioSourceEstimator2D(final List<? extends RssiReadingLocated<S, Point2D>> readings) {
132         super(readings);
133     }
134 
135     /**
136      * Constructor.
137      *
138      * @param listener listener in charge of attending events raised by this instance.
139      */
140     public PROMedSRobustRssiRadioSourceEstimator2D(final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
141         this.listener = listener;
142     }
143 
144     /**
145      * Constructor.
146      * Sets signal readings belonging to the same radio source.
147      *
148      * @param readings signal readings belonging to the same radio source.
149      * @param listener listener in charge of attending events raised by this instance.
150      * @throws IllegalArgumentException if readings are not valid.
151      */
152     public PROMedSRobustRssiRadioSourceEstimator2D(
153             final List<? extends RssiReadingLocated<S, Point2D>> readings,
154             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
155         super(readings, listener);
156     }
157 
158     /**
159      * Constructor.
160      * Sets signal readings belonging to the same radio source.
161      *
162      * @param readings        signal readings belonging to the same radio source.
163      * @param initialPosition initial position to start the estimation of access
164      *                        point position.
165      * @throws IllegalArgumentException if readings are not valid.
166      */
167     public PROMedSRobustRssiRadioSourceEstimator2D(
168             final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition) {
169         super(readings, initialPosition);
170     }
171 
172     /**
173      * Constructor.
174      *
175      * @param initialPosition initial position to start the estimation of access
176      *                        point position.
177      */
178     public PROMedSRobustRssiRadioSourceEstimator2D(final Point2D initialPosition) {
179         super(initialPosition);
180     }
181 
182     /**
183      * Constructor.
184      *
185      * @param initialPosition initial position to start the estimation of access
186      *                        point position.
187      * @param listener        listener in charge of attending events raised by this instance.
188      */
189     public PROMedSRobustRssiRadioSourceEstimator2D(
190             final Point2D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
191         super(initialPosition, listener);
192     }
193 
194     /**
195      * Constructor.
196      * Sets signal readings belonging to the same radio source.
197      *
198      * @param readings        signal readings belonging to the same radio source.
199      * @param initialPosition initial position to start the estimation of radio
200      *                        source position.
201      * @param listener        listener in charge of attending events raised by this instance.
202      * @throws IllegalArgumentException if readings are not valid.
203      */
204     public PROMedSRobustRssiRadioSourceEstimator2D(
205             final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
206             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
207         super(readings, initialPosition, listener);
208     }
209 
210     /**
211      * Constructor.
212      *
213      * @param initialTransmittedPowerdBm initial transmitted power to start the
214      *                                   estimation of radio source transmitted power
215      *                                   (expressed in dBm's)
216      */
217     public PROMedSRobustRssiRadioSourceEstimator2D(final Double initialTransmittedPowerdBm) {
218         super(initialTransmittedPowerdBm);
219     }
220 
221     /**
222      * Constructor.
223      * Sets signal readings belonging to the same radio source.
224      *
225      * @param readings                   signal readings belonging to the same radio source.
226      * @param initialTransmittedPowerdBm initial transmitted power to start the
227      *                                   estimation of radio source transmitted power
228      *                                   (expressed in dBm's)
229      * @throws IllegalArgumentException if readings are not valid.
230      */
231     public PROMedSRobustRssiRadioSourceEstimator2D(
232             final List<? extends RssiReadingLocated<S, Point2D>> readings, final Double initialTransmittedPowerdBm) {
233         super(readings, initialTransmittedPowerdBm);
234     }
235 
236     /**
237      * Constructor.
238      *
239      * @param initialTransmittedPowerdBm initial transmitted power to start the
240      *                                   estimation of radio source transmitted power
241      *                                   (expressed in dBm's)
242      * @param listener                   listener in charge of attending events raised by this instance.
243      */
244     public PROMedSRobustRssiRadioSourceEstimator2D(
245             final Double initialTransmittedPowerdBm,
246             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
247         super(initialTransmittedPowerdBm, listener);
248     }
249 
250     /**
251      * Constructor.
252      * Sets signal readings belonging to the same radio source.
253      *
254      * @param readings                   signal readings belonging to the same radio source.
255      * @param initialTransmittedPowerdBm initial transmitted power to start the
256      *                                   estimation of radio source transmitted power
257      *                                   (expressed in dBm's)
258      * @param listener                   listener in charge of attending events raised by this instance.
259      * @throws IllegalArgumentException if readings are not valid.
260      */
261     public PROMedSRobustRssiRadioSourceEstimator2D(
262             final List<? extends RssiReadingLocated<S, Point2D>> readings, final Double initialTransmittedPowerdBm,
263             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
264         super(readings, initialTransmittedPowerdBm, listener);
265     }
266 
267     /**
268      * Constructor.
269      * Sets signal readings belonging to the same radio source.
270      *
271      * @param readings                   signal readings belonging to the same radio source.
272      * @param initialPosition            initial position to start the estimation of access
273      *                                   point position.
274      * @param initialTransmittedPowerdBm initial transmitted power to start the
275      *                                   estimation of radio source transmitted power
276      *                                   (expressed in dBm's).
277      * @throws IllegalArgumentException if readings are not valid.
278      */
279     public PROMedSRobustRssiRadioSourceEstimator2D(
280             final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
281             final Double initialTransmittedPowerdBm) {
282         super(readings, initialPosition, initialTransmittedPowerdBm);
283     }
284 
285     /**
286      * Constructor.
287      *
288      * @param initialPosition            initial position to start the estimation of radio
289      *                                   source position.
290      * @param initialTransmittedPowerdBm initial transmitted power to start the
291      *                                   estimation of radio source transmitted power
292      *                                   (expressed in dBm's).
293      */
294     public PROMedSRobustRssiRadioSourceEstimator2D(
295             final Point2D initialPosition, final Double initialTransmittedPowerdBm) {
296         super(initialPosition, initialTransmittedPowerdBm);
297     }
298 
299     /**
300      * Constructor.
301      *
302      * @param initialPosition            initial position to start the estimation of radio
303      *                                   source position.
304      * @param initialTransmittedPowerdBm initial transmitted power to start the
305      *                                   estimation of radio source transmitted power
306      *                                   (expressed in dBm's).
307      * @param listener                   in charge of attending events raised by this instance.
308      */
309     public PROMedSRobustRssiRadioSourceEstimator2D(
310             final Point2D initialPosition, final Double initialTransmittedPowerdBm,
311             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
312         super(initialPosition, initialTransmittedPowerdBm, listener);
313     }
314 
315     /**
316      * Constructor.
317      * Sets signal readings belonging to the same radio source.
318      *
319      * @param readings                   Wi-Fi signal readings belonging to the same radio source.
320      * @param initialPosition            initial position to start the estimation of radio
321      *                                   source position.
322      * @param initialTransmittedPowerdBm initial transmitted power to start the
323      *                                   estimation of radio source transmitted power
324      *                                   (expressed in dBm's).
325      * @param listener                   listener in charge of attending events raised by this instance.
326      * @throws IllegalArgumentException if readings are not valid.
327      */
328     public PROMedSRobustRssiRadioSourceEstimator2D(
329             final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
330             Double initialTransmittedPowerdBm, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
331         super(readings, initialPosition, initialTransmittedPowerdBm, listener);
332     }
333 
334     /**
335      * Constructor.
336      * Sets signal readings belonging to the same radio source.
337      *
338      * @param readings                   signal readings belonging to the same radio source.
339      * @param initialPosition            initial position to start the estimation of radio
340      *                                   source position.
341      * @param initialTransmittedPowerdBm initial transmitted power to start the
342      *                                   estimation of radio source transmitted power
343      *                                   (expressed in dBm's).
344      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
345      * @throws IllegalArgumentException if readings are not valid.
346      */
347     public PROMedSRobustRssiRadioSourceEstimator2D(
348             final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
349             Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
350         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
351     }
352 
353     /**
354      * Constructor.
355      *
356      * @param initialPosition            initial position to start the estimation of radio
357      *                                   source position.
358      * @param initialTransmittedPowerdBm initial transmitted power to start the
359      *                                   estimation of radio source transmitted power
360      *                                   (expressed in dBm's).
361      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
362      */
363     public PROMedSRobustRssiRadioSourceEstimator2D(
364             final Point2D initialPosition, final Double initialTransmittedPowerdBm,
365             final double initialPathLossExponent) {
366         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
367     }
368 
369     /**
370      * Constructor.
371      *
372      * @param initialPosition            initial position to start the estimation of radio
373      *                                   source position.
374      * @param initialTransmittedPowerdBm initial transmitted power to start the
375      *                                   estimation of radio source transmitted power
376      *                                   (expressed in dBm's).
377      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
378      * @param listener                   listener in charge of attending events raised by this instance.
379      */
380     public PROMedSRobustRssiRadioSourceEstimator2D(
381             final Point2D initialPosition, final Double initialTransmittedPowerdBm,
382             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
383         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
384     }
385 
386     /**
387      * Constructor.
388      * Sets signal readings belonging to the same radio source.
389      *
390      * @param readings                   signal readings belonging to the same radio source.
391      * @param initialPosition            initial position to start the estimation of radio
392      *                                   source position.
393      * @param initialTransmittedPowerdBm initial transmitted power to start the
394      *                                   estimation of radio source transmitted power
395      *                                   (expressed in dBm's).
396      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
397      * @param listener                   listener in charge of attending events raised by this instance.
398      * @throws IllegalArgumentException if readings are not valid.
399      */
400     public PROMedSRobustRssiRadioSourceEstimator2D(
401             final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
402             final Double initialTransmittedPowerdBm, final double initialPathLossExponent,
403             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
404         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
405     }
406 
407     /**
408      * Constructor.
409      *
410      * @param qualityScores quality scores corresponding to each provided
411      *                      sample. The larger the score value the better
412      *                      the quality of the sample.
413      * @throws IllegalArgumentException if quality scores is null, or length
414      *                                  of quality scores is less than required minimum.
415      */
416     public PROMedSRobustRssiRadioSourceEstimator2D(final double[] qualityScores) {
417         super();
418         internalSetQualityScores(qualityScores);
419     }
420 
421     /**
422      * Constructor.
423      * Sets signal readings belonging to the same radio source.
424      *
425      * @param qualityScores quality scores corresponding to each provided
426      *                      sample. The larger the score value the better
427      *                      the quality of the sample.
428      * @param readings      signal readings belonging to the same radio source.
429      * @throws IllegalArgumentException if readings are not valid, quality scores
430      *                                  is null, or length of quality scores is less than required minimum.
431      */
432     public PROMedSRobustRssiRadioSourceEstimator2D(
433             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings) {
434         super(readings);
435         internalSetQualityScores(qualityScores);
436     }
437 
438     /**
439      * Constructor.
440      *
441      * @param qualityScores quality scores corresponding to each provided
442      *                      sample. The larger the score value the better
443      *                      the quality of the sample.
444      * @param listener      listener in charge of attending events raised by this instance.
445      * @throws IllegalArgumentException if quality scores is null, or length
446      *                                  of quality scores is less than required minimum.
447      */
448     public PROMedSRobustRssiRadioSourceEstimator2D(
449             final double[] qualityScores, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
450         super(listener);
451         internalSetQualityScores(qualityScores);
452     }
453 
454     /**
455      * Constructor.
456      * Sets signal readings belonging to the same radio source.
457      *
458      * @param qualityScores quality scores corresponding to each provided
459      *                      sample. The larger the score value the better
460      *                      the quality of the sample.
461      * @param readings      signal readings belonging to the same radio source.
462      * @param listener      listener in charge of attending events raised by this instance.
463      * @throws IllegalArgumentException if readings are not valid, quality scores
464      *                                  is null, or length of quality scores is less than required minimum.
465      */
466     public PROMedSRobustRssiRadioSourceEstimator2D(
467             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
468             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
469         super(readings, listener);
470         internalSetQualityScores(qualityScores);
471     }
472 
473     /**
474      * Constructor.
475      * Sets signal readings belonging to the same radio source.
476      *
477      * @param qualityScores   quality scores corresponding to each provided
478      *                        sample. The larger the score value the better
479      *                        the quality of the sample.
480      * @param readings        signal readings belonging to the same radio source.
481      * @param initialPosition initial position to start the estimation of radio
482      *                        source position.
483      * @throws IllegalArgumentException if readings are not valid, quality scores
484      *                                  is null, or length of quality scores is less than required minimum.
485      */
486     public PROMedSRobustRssiRadioSourceEstimator2D(
487             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
488             final Point2D initialPosition) {
489         super(readings, initialPosition);
490         internalSetQualityScores(qualityScores);
491     }
492 
493     /**
494      * Constructor.
495      *
496      * @param qualityScores   quality scores corresponding to each provided
497      *                        sample. The larger the score value the better
498      *                        the quality of the sample.
499      * @param initialPosition initial position to start the estimation of radio
500      *                        source position.
501      */
502     public PROMedSRobustRssiRadioSourceEstimator2D(final double[] qualityScores, final Point2D initialPosition) {
503         super(initialPosition);
504         internalSetQualityScores(qualityScores);
505     }
506 
507     /**
508      * Constructor.
509      *
510      * @param qualityScores   quality scores corresponding to each provided
511      *                        sample. The larger the score value the better
512      *                        the quality of the sample.
513      * @param initialPosition initial position to start the estimation of radio
514      *                        source position.
515      * @param listener        listener in charge of attending events raised by this instance.
516      * @throws IllegalArgumentException if quality scores is null, or length
517      *                                  of quality scores is less than required minimum.
518      */
519     public PROMedSRobustRssiRadioSourceEstimator2D(
520             final double[] qualityScores, final Point2D initialPosition,
521             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
522         super(initialPosition, listener);
523         internalSetQualityScores(qualityScores);
524     }
525 
526     /**
527      * Constructor.
528      * Sets signal readings belonging to the same radio source.
529      *
530      * @param qualityScores   quality scores corresponding to each provided
531      *                        sample. The larger the score value the better
532      *                        the quality of the sample.
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 listener        listener in charge of attending events raised by this instance.
537      * @throws IllegalArgumentException if readings are not valid, quality scores
538      *                                  is null, or length of quality scores is less than required minimum.
539      */
540     public PROMedSRobustRssiRadioSourceEstimator2D(
541             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
542             final Point2D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
543         super(readings, initialPosition, listener);
544         internalSetQualityScores(qualityScores);
545     }
546 
547     /**
548      * Constructor.
549      *
550      * @param qualityScores              quality scores corresponding to each provided
551      *                                   sample. The larger the score value the better
552      *                                   the quality of the sample.
553      * @param initialTransmittedPowerdBm initial transmitted power to start the
554      *                                   estimation of radio source transmitted power
555      *                                   (expressed in dBm's)
556      * @throws IllegalArgumentException if quality scores is null, or length
557      *                                  of quality scores is less than required minimum.
558      */
559     public PROMedSRobustRssiRadioSourceEstimator2D(
560             final double[] qualityScores, final Double initialTransmittedPowerdBm) {
561         super(initialTransmittedPowerdBm);
562         internalSetQualityScores(qualityScores);
563     }
564 
565     /**
566      * Constructor.
567      * Sets signal readings belonging to the same radio source.
568      *
569      * @param qualityScores              quality scores corresponding to each provided
570      *                                   sample. The larger the score value the better
571      *                                   the quality of the sample.
572      * @param readings                   signal readings belonging to the same radio source.
573      * @param initialTransmittedPowerdBm initial transmitted power to start the
574      *                                   estimation of radio source transmitted power
575      *                                   (expressed in dBm's)
576      * @throws IllegalArgumentException if readings are not valid, quality scores
577      *                                  is null, or length of quality scores is less than required minimum.
578      */
579     public PROMedSRobustRssiRadioSourceEstimator2D(
580             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
581             final Double initialTransmittedPowerdBm) {
582         super(readings, initialTransmittedPowerdBm);
583         internalSetQualityScores(qualityScores);
584     }
585 
586     /**
587      * Constructor.
588      *
589      * @param qualityScores              quality scores corresponding to each provided
590      *                                   sample. The larger the score value the better
591      *                                   the quality of the sample.
592      * @param initialTransmittedPowerdBm initial transmitted power to start the
593      *                                   estimation of radio source transmitted power
594      *                                   (expressed in dBm's)
595      * @param listener                   listener in charge of attending events raised by this instance.
596      * @throws IllegalArgumentException if quality scores is null, or length
597      *                                  of quality scores is less than required minimum.
598      */
599     public PROMedSRobustRssiRadioSourceEstimator2D(
600             final double[] qualityScores, final Double initialTransmittedPowerdBm,
601             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
602         super(initialTransmittedPowerdBm, listener);
603         internalSetQualityScores(qualityScores);
604     }
605 
606     /**
607      * Constructor.
608      * Sets signal readings belonging to the same radio source.
609      *
610      * @param qualityScores              quality scores corresponding to each provided
611      *                                   sample. The larger the score value the better
612      *                                   the quality of the sample.
613      * @param readings                   signal readings belonging to the same radio source.
614      * @param initialTransmittedPowerdBm initial transmitted power to start the
615      *                                   estimation of radio source transmitted power
616      *                                   (expressed in dBm's)
617      * @param listener                   listener in charge of attending events raised by this instance.
618      * @throws IllegalArgumentException if readings are not valid, quality scores
619      *                                  is null, or length of quality scores is less than required minimum.
620      */
621     public PROMedSRobustRssiRadioSourceEstimator2D(
622             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
623             final Double initialTransmittedPowerdBm,
624             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
625         super(readings, initialTransmittedPowerdBm, listener);
626         internalSetQualityScores(qualityScores);
627     }
628 
629     /**
630      * Constructor.
631      * Sets signal readings belonging to the same radio source.
632      *
633      * @param qualityScores              quality scores corresponding to each provided
634      *                                   sample. The larger the score value the better
635      *                                   the quality of the sample.
636      * @param readings                   signal readings belonging to the same radio source.
637      * @param initialPosition            initial position to start the estimation of radio
638      *                                   source position.
639      * @param initialTransmittedPowerdBm initial transmitted power to start the
640      *                                   estimation of radio source transmitted power
641      *                                   (expressed in dBm's).
642      * @throws IllegalArgumentException if readings are not valid, quality scores
643      *                                  is null, or length of quality scores is less than required minimum.
644      */
645     public PROMedSRobustRssiRadioSourceEstimator2D(
646             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
647             final Point2D initialPosition, final Double initialTransmittedPowerdBm) {
648         super(readings, initialPosition, initialTransmittedPowerdBm);
649         internalSetQualityScores(qualityScores);
650     }
651 
652     /**
653      * Constructor.
654      *
655      * @param qualityScores              quality scores corresponding to each provided
656      *                                   sample. The larger the score value the better
657      *                                   the quality of the sample.
658      * @param initialPosition            initial position to start the estimation of radio
659      *                                   source position.
660      * @param initialTransmittedPowerdBm initial transmitted power to start the
661      *                                   estimation of radio source transmitted power
662      *                                   (expressed in dBm's).
663      * @throws IllegalArgumentException if quality scores is null, or length
664      *                                  of quality scores is less than required minimum.
665      */
666     public PROMedSRobustRssiRadioSourceEstimator2D(
667             final double[] qualityScores, final Point2D initialPosition, final Double initialTransmittedPowerdBm) {
668         super(initialPosition, initialTransmittedPowerdBm);
669         internalSetQualityScores(qualityScores);
670     }
671 
672     /**
673      * Constructor.
674      *
675      * @param qualityScores              quality scores corresponding to each provided
676      *                                   sample. The larger the score value the better
677      *                                   the quality of the sample.
678      * @param initialPosition            initial position to start the estimation of radio
679      *                                   source position.
680      * @param initialTransmittedPowerdBm initial transmitted power to start the
681      *                                   estimation of radio source transmitted power
682      *                                   (expressed in dBm's).
683      * @param listener                   in charge of attending events raised by this instance.
684      * @throws IllegalArgumentException if quality scores is null, or length
685      *                                  of quality scores is less than required minimum.
686      */
687     public PROMedSRobustRssiRadioSourceEstimator2D(
688             final double[] qualityScores, final Point2D initialPosition, final Double initialTransmittedPowerdBm,
689             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
690         super(initialPosition, initialTransmittedPowerdBm, listener);
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
699      *                                   sample. The larger the score value the better
700      *                                   the quality of the sample.
701      * @param readings                   signal readings belonging to the same radio source.
702      * @param initialPosition            initial position to start the estimation of radio
703      *                                   source position.
704      * @param initialTransmittedPowerdBm initial transmitted power to start the
705      *                                   estimation of radio source transmitted power
706      *                                   (expressed in dBm's).
707      * @param listener                   listener in charge of attending events raised by this instance.
708      * @throws IllegalArgumentException if readings are not valid, quality scores
709      *                                  is null, or length of quality scores is less than required minimum.
710      */
711     public PROMedSRobustRssiRadioSourceEstimator2D(
712             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
713             final Point2D initialPosition, final Double initialTransmittedPowerdBm,
714             final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
715         super(readings, initialPosition, initialTransmittedPowerdBm, listener);
716         internalSetQualityScores(qualityScores);
717     }
718 
719     /**
720      * Constructor.
721      * Sets signal readings belonging to the same radio source.
722      *
723      * @param qualityScores              quality scores corresponding to each provided
724      *                                   sample. The larger the score value the better
725      *                                   the quality of the sample.
726      * @param readings                   signal readings belonging to the same radio source.
727      * @param initialPosition            initial position to start the estimation of radio
728      *                                   source position.
729      * @param initialTransmittedPowerdBm initial transmitted power to start the
730      *                                   estimation of radio source transmitted power
731      *                                   (expressed in dBm's).
732      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
733      * @throws IllegalArgumentException if readings are not valid, quality scores
734      *                                  is null, or length of quality scores is less than required minimum.
735      */
736     public PROMedSRobustRssiRadioSourceEstimator2D(
737             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
738             final Point2D initialPosition, final Double initialTransmittedPowerdBm,
739             final double initialPathLossExponent) {
740         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
741         internalSetQualityScores(qualityScores);
742     }
743 
744     /**
745      * Constructor.
746      *
747      * @param qualityScores              quality scores corresponding to each provided
748      *                                   sample. The larger the score value the better
749      *                                   the quality of the sample.
750      * @param initialPosition            initial position to start the estimation of radio
751      *                                   source position.
752      * @param initialTransmittedPowerdBm initial transmitted power to start the
753      *                                   estimation of radio source transmitted power
754      *                                   (expressed in dBm's).
755      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
756      */
757     public PROMedSRobustRssiRadioSourceEstimator2D(
758             final double[] qualityScores, final Point2D initialPosition, final Double initialTransmittedPowerdBm,
759             final double initialPathLossExponent) {
760         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
761         internalSetQualityScores(qualityScores);
762     }
763 
764     /**
765      * Constructor.
766      *
767      * @param qualityScores              quality scores corresponding to each provided
768      *                                   sample. The larger the score value the better
769      *                                   the quality of the sample.
770      * @param initialPosition            initial position to start the estimation of radio
771      *                                   source position.
772      * @param initialTransmittedPowerdBm initial transmitted power to start the
773      *                                   estimation of radio source transmitted power
774      *                                   (expressed in dBm's).
775      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
776      * @param listener                   listener in charge of attending events raised by this instance.
777      */
778     public PROMedSRobustRssiRadioSourceEstimator2D(
779             final double[] qualityScores, final Point2D initialPosition, final Double initialTransmittedPowerdBm,
780             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
781         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
782         internalSetQualityScores(qualityScores);
783     }
784 
785     /**
786      * Constructor.
787      * Sets signal readings belonging to the same radio source.
788      *
789      * @param qualityScores              quality scores corresponding to each provided
790      *                                   sample. The larger the score value the better
791      *                                   the quality of the sample.
792      * @param readings                   signal readings belonging to the same radio source.
793      * @param initialPosition            initial position to start the estimation of radio
794      *                                   source position.
795      * @param initialTransmittedPowerdBm initial transmitted power to start the
796      *                                   estimation of radio source transmitted power
797      *                                   (expressed in dBm's).
798      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
799      * @param listener                   listener in charge of attending events raised by this instance.
800      * @throws IllegalArgumentException if readings are not valid, quality scores
801      *                                  is null, or length of quality scores is less than required minimum.
802      */
803     public PROMedSRobustRssiRadioSourceEstimator2D(
804             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
805             final Point2D initialPosition, final Double initialTransmittedPowerdBm,
806             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
807         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
808         internalSetQualityScores(qualityScores);
809     }
810 
811     /**
812      * Returns threshold to be used to keep the algorithm iterating in case that
813      * best estimated threshold using median of residuals is not small enough.
814      * Once a solution is found that generates a threshold below this value, the
815      * algorithm will stop.
816      * The stop threshold can be used to prevent the LMedS algorithm to iterate
817      * too many times in cases where samples have a very similar accuracy.
818      * For instance, in cases where proportion of outliers is very small (close
819      * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
820      * iterate for a long time trying to find the best solution when indeed
821      * there is no need to do that if a reasonable threshold has already been
822      * reached.
823      * Because of this behaviour the stop threshold can be set to a value much
824      * lower than the one typically used in RANSAC, and yet the algorithm could
825      * still produce even smaller thresholds in estimated results.
826      *
827      * @return stop threshold to stop the algorithm prematurely when a certain
828      * accuracy has been reached.
829      */
830     public double getStopThreshold() {
831         return stopThreshold;
832     }
833 
834     /**
835      * Sets threshold to be used to keep the algorithm iterating in case that
836      * best estimated threshold using median of residuals is not small enough.
837      * Once a solution is found that generates a threshold below this value,
838      * the algorithm will stop.
839      * The stop threshold can be used to prevent the LMedS algorithm to iterate
840      * too many times in cases where samples have a very similar accuracy.
841      * For instance, in cases where proportion of outliers is very small (close
842      * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
843      * iterate for a long time trying to find the best solution when indeed
844      * there is no need to do that if a reasonable threshold has already been
845      * reached.
846      * Because of this behaviour the stop threshold can be set to a value much
847      * lower than the one typically used in RANSAC, and yet the algorithm could
848      * still produce even smaller thresholds in estimated results.
849      *
850      * @param stopThreshold stop threshold to stop the algorithm prematurely
851      *                      when a certain accuracy has been reached.
852      * @throws IllegalArgumentException if provided value is zero or negative.
853      * @throws LockedException          if this solver is locked.
854      */
855     public void setStopThreshold(final double stopThreshold) throws LockedException {
856         if (isLocked()) {
857             throw new LockedException();
858         }
859         if (stopThreshold <= MIN_STOP_THRESHOLD) {
860             throw new IllegalArgumentException();
861         }
862 
863         this.stopThreshold = stopThreshold;
864     }
865 
866     /**
867      * Returns quality scores corresponding to each pair of
868      * positions and distances (i.e. sample).
869      * The larger the score value the better the quality of the sample.
870      * This implementation always returns null.
871      * Subclasses using quality scores must implement proper behavior.
872      *
873      * @return quality scores corresponding to each sample.
874      */
875     @Override
876     public double[] getQualityScores() {
877         return qualityScores;
878     }
879 
880     /**
881      * Sets quality scores corresponding to each pair of positions and
882      * distances (i.e. sample).
883      * The larger the score value the better the quality of the sample.
884      * This implementation makes no action.
885      * Subclasses using quality scores must implement proper behaviour.
886      *
887      * @param qualityScores quality scores corresponding to each pair of
888      *                      matched points.
889      * @throws IllegalArgumentException if provided quality scores length
890      *                                  is smaller than minimum required samples.
891      * @throws LockedException          if robust solver is locked because an
892      *                                  estimation is already in progress.
893      */
894     @Override
895     public void setQualityScores(final double[] qualityScores) throws LockedException {
896         if (isLocked()) {
897             throw new LockedException();
898         }
899         internalSetQualityScores(qualityScores);
900     }
901 
902     /**
903      * Indicates whether solver is ready to find a solution.
904      *
905      * @return true if solver is ready, false otherwise.
906      */
907     @Override
908     public boolean isReady() {
909         return super.isReady() && qualityScores != null && qualityScores.length == readings.size();
910     }
911 
912     /**
913      * Robustly estimates position, transmitted power and path-loss exponent for a
914      * radio source.
915      *
916      * @throws LockedException          if instance is busy during estimation.
917      * @throws NotReadyException        if estimator is not ready.
918      * @throws RobustEstimatorException if estimation fails for any reason
919      *                                  (i.e. numerical instability, no solution available, etc).
920      */
921     @Override
922     public void estimate() throws LockedException, NotReadyException, RobustEstimatorException {
923         if (isLocked()) {
924             throw new LockedException();
925         }
926         if (!isReady()) {
927             throw new NotReadyException();
928         }
929 
930         final var innerEstimator = new PROMedSRobustEstimator<>(
931                 new PROMedSRobustEstimatorListener<Solution<Point2D>>() {
932 
933                     @Override
934                     public double[] getQualityScores() {
935                         return qualityScores;
936                     }
937 
938                     @Override
939                     public double getThreshold() {
940                         return stopThreshold;
941                     }
942 
943                     @Override
944                     public int getTotalSamples() {
945                         return readings.size();
946                     }
947 
948                     @Override
949                     public int getSubsetSize() {
950                         return Math.max(preliminarySubsetSize, getMinReadings());
951                     }
952 
953                     @Override
954                     public void estimatePreliminarSolutions(
955                             final int[] samplesIndices, final List<Solution<Point2D>> solutions) {
956                         solvePreliminarySolutions(samplesIndices, solutions);
957                     }
958 
959                     @Override
960                     public double computeResidual(final Solution<Point2D> currentEstimation, int i) {
961                         return residual(currentEstimation, i);
962                     }
963 
964                     @Override
965                     public boolean isReady() {
966                         return PROMedSRobustRssiRadioSourceEstimator2D.this.isReady();
967                     }
968 
969                     @Override
970                     public void onEstimateStart(final RobustEstimator<Solution<Point2D>> estimator) {
971                         // no action needed
972                     }
973 
974                     @Override
975                     public void onEstimateEnd(final RobustEstimator<Solution<Point2D>> estimator) {
976                         // no action needed
977                     }
978 
979                     @Override
980                     public void onEstimateNextIteration(
981                             final RobustEstimator<Solution<Point2D>> estimator, final int iteration) {
982                         if (listener != null) {
983                             listener.onEstimateNextIteration(
984                                     PROMedSRobustRssiRadioSourceEstimator2D.this, iteration);
985                         }
986                     }
987 
988                     @Override
989                     public void onEstimateProgressChange(
990                             final RobustEstimator<Solution<Point2D>> estimator, final float progress) {
991                         if (listener != null) {
992                             listener.onEstimateProgressChange(
993                                     PROMedSRobustRssiRadioSourceEstimator2D.this, progress);
994                         }
995                     }
996                 });
997 
998         try {
999             locked = true;
1000 
1001             if (listener != null) {
1002                 listener.onEstimateStart(this);
1003             }
1004 
1005             inliersData = null;
1006 
1007             // inlier thresholds are disable to obtain a less restrictive amount of inliers
1008             innerEstimator.setUseInlierThresholds(false);
1009 
1010             innerEstimator.setConfidence(confidence);
1011             innerEstimator.setMaxIterations(maxIterations);
1012             innerEstimator.setProgressDelta(progressDelta);
1013             final var result = innerEstimator.estimate();
1014             inliersData = innerEstimator.getInliersData();
1015             attemptRefine(result);
1016 
1017             if (listener != null) {
1018                 listener.onEstimateEnd(this);
1019             }
1020 
1021         } catch (final com.irurueta.numerical.LockedException e) {
1022             throw new LockedException(e);
1023         } catch (final com.irurueta.numerical.NotReadyException e) {
1024             throw new NotReadyException(e);
1025         } finally {
1026             locked = false;
1027         }
1028     }
1029 
1030     /**
1031      * Returns method being used for robust estimation.
1032      *
1033      * @return method being used for robust estimation.
1034      */
1035     @Override
1036     public RobustEstimatorMethod getMethod() {
1037         return RobustEstimatorMethod.PROMEDS;
1038     }
1039 
1040     /**
1041      * Sets quality scores corresponding to each provided sample.
1042      * This method is used internally and does not check whether instance is
1043      * locked or not.
1044      *
1045      * @param qualityScores quality scores to be set.
1046      * @throws IllegalArgumentException if provided quality scores length
1047      *                                  is smaller than 3 samples.
1048      */
1049     private void internalSetQualityScores(final double[] qualityScores) {
1050         if (qualityScores == null || qualityScores.length < getMinReadings()) {
1051             throw new IllegalArgumentException();
1052         }
1053 
1054         this.qualityScores = qualityScores;
1055     }
1056 }