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.Point3D;
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 3D 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 PROMedSRobustRssiRadioSourceEstimator3D<S extends RadioSource> extends
67          RobustRssiRadioSourceEstimator3D<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 PROMedSRobustRssiRadioSourceEstimator3D() {
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 PROMedSRobustRssiRadioSourceEstimator3D(final List<? extends RssiReadingLocated<S, Point3D>> 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 PROMedSRobustRssiRadioSourceEstimator3D(final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROMedSRobustRssiRadioSourceEstimator3D(
153             final List<? extends RssiReadingLocated<S, Point3D>> readings,
154             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 radio
164      *                        source position.
165      * @throws IllegalArgumentException if readings are not valid.
166      */
167     public PROMedSRobustRssiRadioSourceEstimator3D(
168             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D 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 PROMedSRobustRssiRadioSourceEstimator3D(final Point3D initialPosition) {
179         super(initialPosition);
180     }
181 
182     /**
183      * Constructor.
184      *
185      * @param initialPosition initial position to start the estimation of radio
186      *                        source position.
187      * @param listener        listener in charge of attending events raised by this instance.
188      */
189     public PROMedSRobustRssiRadioSourceEstimator3D(
190             final Point3D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROMedSRobustRssiRadioSourceEstimator3D(
205             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
206             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
207         super(readings, initialPosition, listener);
208     }
209 
210     /**
211      * Constructor.
212      *
213      * @param initialTransmittedPowerdBm initial transmitted power to start the
214      *                                   estimation of access point transmitted power
215      *                                   (expressed in dBm's)
216      */
217     public PROMedSRobustRssiRadioSourceEstimator3D(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 PROMedSRobustRssiRadioSourceEstimator3D(
232             final List<? extends RssiReadingLocated<S, Point3D>> 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 PROMedSRobustRssiRadioSourceEstimator3D(
245             final Double initialTransmittedPowerdBm,
246             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROMedSRobustRssiRadioSourceEstimator3D(
262             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Double initialTransmittedPowerdBm,
263             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 radio
273      *                                   source 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 PROMedSRobustRssiRadioSourceEstimator3D(
280             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D 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 PROMedSRobustRssiRadioSourceEstimator3D(
295             final Point3D 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 PROMedSRobustRssiRadioSourceEstimator3D(
310             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
311             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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                   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 PROMedSRobustRssiRadioSourceEstimator3D(
329             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
330             final Double initialTransmittedPowerdBm,
331             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
332         super(readings, initialPosition, initialTransmittedPowerdBm, listener);
333     }
334 
335     /**
336      * Constructor.
337      * Sets signal readings belonging to the same radio source.
338      *
339      * @param readings                   signal readings belonging to the same radio source.
340      * @param initialPosition            initial position to start the estimation of radio
341      *                                   source position.
342      * @param initialTransmittedPowerdBm initial transmitted power to start the
343      *                                   estimation of radio source transmitted power
344      *                                   (expressed in dBm's).
345      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
346      * @throws IllegalArgumentException if readings are not valid.
347      */
348     public PROMedSRobustRssiRadioSourceEstimator3D(
349             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
350             final Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
351         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
352     }
353 
354     /**
355      * Constructor.
356      *
357      * @param initialPosition            initial position to start the estimation of radio
358      *                                   source position.
359      * @param initialTransmittedPowerdBm initial transmitted power to start the
360      *                                   estimation of radio source transmitted power
361      *                                   (expressed in dBm's).
362      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
363      */
364     public PROMedSRobustRssiRadioSourceEstimator3D(
365             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
366             final double initialPathLossExponent) {
367         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
368     }
369 
370     /**
371      * Constructor.
372      *
373      * @param initialPosition            initial position to start the estimation of radio
374      *                                   source position.
375      * @param initialTransmittedPowerdBm initial transmitted power to start the
376      *                                   estimation of radio source transmitted power
377      *                                   (expressed in dBm's).
378      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
379      * @param listener                   listener in charge of attending events raised by this instance.
380      */
381     public PROMedSRobustRssiRadioSourceEstimator3D(
382             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
383             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
384         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
385     }
386 
387     /**
388      * Constructor.
389      * Sets signal readings belonging to the same radio source.
390      *
391      * @param readings                   signal readings belonging to the same radio source.
392      * @param initialPosition            initial position to start the estimation of radio
393      *                                   source position.
394      * @param initialTransmittedPowerdBm initial transmitted power to start the
395      *                                   estimation of radio source transmitted power
396      *                                   (expressed in dBm's).
397      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
398      * @param listener                   listener in charge of attending events raised by this instance.
399      * @throws IllegalArgumentException if readings are not valid.
400      */
401     public PROMedSRobustRssiRadioSourceEstimator3D(
402             final List<? extends RssiReadingLocated<S, Point3D>> readings,
403             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
404             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
405         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
406     }
407 
408     /**
409      * Constructor.
410      *
411      * @param qualityScores quality scores corresponding to each provided
412      *                      sample. The larger the score value the better
413      *                      the quality of the sample.
414      * @throws IllegalArgumentException if quality scores is null, or length
415      *                                  of quality scores is less than required minimum.
416      */
417     public PROMedSRobustRssiRadioSourceEstimator3D(final double[] qualityScores) {
418         super();
419         internalSetQualityScores(qualityScores);
420     }
421 
422     /**
423      * Constructor.
424      * Sets signal readings belonging to the same radio source.
425      *
426      * @param qualityScores quality scores corresponding to each provided
427      *                      sample. The larger the score value the better
428      *                      the quality of the sample.
429      * @param readings      signal readings belonging to the same radio source.
430      * @throws IllegalArgumentException if readings are not valid, quality scores
431      *                                  is null, or length of quality scores is less than required minimum.
432      */
433     public PROMedSRobustRssiRadioSourceEstimator3D(
434             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings) {
435         super(readings);
436         internalSetQualityScores(qualityScores);
437     }
438 
439     /**
440      * Constructor.
441      *
442      * @param qualityScores quality scores corresponding to each provided
443      *                      sample. The larger the score value the better
444      *                      the quality of the sample.
445      * @param listener      listener in charge of attending events raised by this instance.
446      * @throws IllegalArgumentException if quality scores is null, or length
447      *                                  of quality scores is less than required minimum.
448      */
449     public PROMedSRobustRssiRadioSourceEstimator3D(
450             final double[] qualityScores, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
451         super(listener);
452         internalSetQualityScores(qualityScores);
453     }
454 
455     /**
456      * Constructor.
457      * Sets signal readings belonging to the same radio source.
458      *
459      * @param qualityScores quality scores corresponding to each provided
460      *                      sample. The larger the score value the better
461      *                      the quality of the sample.
462      * @param readings      signal readings belonging to the same radio source.
463      * @param listener      listener in charge of attending events raised by this instance.
464      * @throws IllegalArgumentException if readings are not valid, quality scores
465      *                                  is null, or length of quality scores is less than required minimum.
466      */
467     public PROMedSRobustRssiRadioSourceEstimator3D(
468             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
469             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
470         super(readings, listener);
471         internalSetQualityScores(qualityScores);
472     }
473 
474     /**
475      * Constructor.
476      * Sets signal readings belonging to the same radio source.
477      *
478      * @param qualityScores   quality scores corresponding to each provided
479      *                        sample. The larger the score value the better
480      *                        the quality of the sample.
481      * @param readings        signal readings belonging to the same radio source.
482      * @param initialPosition initial position to start the estimation of access
483      *                        point position.
484      * @throws IllegalArgumentException if readings are not valid, quality scores
485      *                                  is null, or length of quality scores is less than required minimum.
486      */
487     public PROMedSRobustRssiRadioSourceEstimator3D(
488             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
489             final Point3D initialPosition) {
490         super(readings, initialPosition);
491         internalSetQualityScores(qualityScores);
492     }
493 
494     /**
495      * Constructor.
496      *
497      * @param qualityScores   quality scores corresponding to each provided
498      *                        sample. The larger the score value the better
499      *                        the quality of the sample.
500      * @param initialPosition initial position to start the estimation of radio
501      *                        source position.
502      */
503     public PROMedSRobustRssiRadioSourceEstimator3D(final double[] qualityScores, final Point3D initialPosition) {
504         super(initialPosition);
505         internalSetQualityScores(qualityScores);
506     }
507 
508     /**
509      * Constructor.
510      *
511      * @param qualityScores   quality scores corresponding to each provided
512      *                        sample. The larger the score value the better
513      *                        the quality of the sample.
514      * @param initialPosition initial position to start the estimation of radio
515      *                        source position.
516      * @param listener        listener in charge of attending events raised by this instance.
517      * @throws IllegalArgumentException if quality scores is null, or length
518      *                                  of quality scores is less than required minimum.
519      */
520     public PROMedSRobustRssiRadioSourceEstimator3D(
521             final double[] qualityScores, final Point3D initialPosition,
522             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
523         super(initialPosition, listener);
524         internalSetQualityScores(qualityScores);
525     }
526 
527     /**
528      * Constructor.
529      * Sets signal readings belonging to the same radio source.
530      *
531      * @param qualityScores   quality scores corresponding to each provided
532      *                        sample. The larger the score value the better
533      *                        the quality of the sample.
534      * @param readings        signal readings belonging to the same radio source.
535      * @param initialPosition initial position to start the estimation of radio
536      *                        source position.
537      * @param listener        listener in charge of attending events raised by this instance.
538      * @throws IllegalArgumentException if readings are not valid, quality scores
539      *                                  is null, or length of quality scores is less than required minimum.
540      */
541     public PROMedSRobustRssiRadioSourceEstimator3D(
542             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
543             final Point3D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
544         super(readings, initialPosition, listener);
545         internalSetQualityScores(qualityScores);
546     }
547 
548     /**
549      * Constructor.
550      *
551      * @param qualityScores              quality scores corresponding to each provided
552      *                                   sample. The larger the score value the better
553      *                                   the quality of the sample.
554      * @param initialTransmittedPowerdBm initial transmitted power to start the
555      *                                   estimation of access point transmitted power
556      *                                   (expressed in dBm's)
557      * @throws IllegalArgumentException if quality scores is null, or length
558      *                                  of quality scores is less than required minimum.
559      */
560     public PROMedSRobustRssiRadioSourceEstimator3D(
561             final double[] qualityScores, final Double initialTransmittedPowerdBm) {
562         super(initialTransmittedPowerdBm);
563         internalSetQualityScores(qualityScores);
564     }
565 
566     /**
567      * Constructor.
568      * Sets signal readings belonging to the same radio source.
569      *
570      * @param qualityScores              quality scores corresponding to each provided
571      *                                   sample. The larger the score value the better
572      *                                   the quality of the sample.
573      * @param readings                   signal readings belonging to the same radio source.
574      * @param initialTransmittedPowerdBm initial transmitted power to start the
575      *                                   estimation of radio source transmitted power
576      *                                   (expressed in dBm's)
577      * @throws IllegalArgumentException if readings are not valid, quality scores
578      *                                  is null, or length of quality scores is less than required minimum.
579      */
580     public PROMedSRobustRssiRadioSourceEstimator3D(
581             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
582             final Double initialTransmittedPowerdBm) {
583         super(readings, initialTransmittedPowerdBm);
584         internalSetQualityScores(qualityScores);
585     }
586 
587     /**
588      * Constructor.
589      *
590      * @param qualityScores              quality scores corresponding to each provided
591      *                                   sample. The larger the score value the better
592      *                                   the quality of the sample.
593      * @param initialTransmittedPowerdBm initial transmitted power to start the
594      *                                   estimation of access point transmitted power
595      *                                   (expressed in dBm's)
596      * @param listener                   listener in charge of attending events raised by this instance.
597      * @throws IllegalArgumentException if quality scores is null, or length
598      *                                  of quality scores is less than required minimum.
599      */
600     public PROMedSRobustRssiRadioSourceEstimator3D(
601             final double[] qualityScores, final Double initialTransmittedPowerdBm,
602             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
603         super(initialTransmittedPowerdBm, listener);
604         internalSetQualityScores(qualityScores);
605     }
606 
607     /**
608      * Constructor.
609      * Sets signal readings belonging to the same radio source.
610      *
611      * @param qualityScores              quality scores corresponding to each provided
612      *                                   sample. The larger the score value the better
613      *                                   the quality of the sample.
614      * @param readings                   signal readings belonging to the same radio source.
615      * @param initialTransmittedPowerdBm initial transmitted power to start the
616      *                                   estimation of radio source transmitted power
617      *                                   (expressed in dBm's)
618      * @param listener                   listener in charge of attending events raised by this instance.
619      * @throws IllegalArgumentException if readings are not valid, quality scores
620      *                                  is null, or length of quality scores is less than required minimum.
621      */
622     public PROMedSRobustRssiRadioSourceEstimator3D(
623             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
624             final Double initialTransmittedPowerdBm,
625             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
626         super(readings, initialTransmittedPowerdBm, listener);
627         internalSetQualityScores(qualityScores);
628     }
629 
630     /**
631      * Constructor.
632      * Sets signal readings belonging to the same radio source.
633      *
634      * @param qualityScores              quality scores corresponding to each provided
635      *                                   sample. The larger the score value the better
636      *                                   the quality of the sample.
637      * @param readings                   signal readings belonging to the same radio source.
638      * @param initialPosition            initial position to start the estimation of access
639      *                                   point position.
640      * @param initialTransmittedPowerdBm initial transmitted power to start the
641      *                                   estimation of radio source transmitted power
642      *                                   (expressed in dBm's).
643      * @throws IllegalArgumentException if readings are not valid, quality scores
644      *                                  is null, or length of quality scores is less than required minimum.
645      */
646     public PROMedSRobustRssiRadioSourceEstimator3D(
647             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
648             final Point3D initialPosition, final Double initialTransmittedPowerdBm) {
649         super(readings, initialPosition, initialTransmittedPowerdBm);
650         internalSetQualityScores(qualityScores);
651     }
652 
653     /**
654      * Constructor.
655      *
656      * @param qualityScores              quality scores corresponding to each provided
657      *                                   sample. The larger the score value the better
658      *                                   the quality of the sample.
659      * @param initialPosition            initial position to start the estimation of radio
660      *                                   source position.
661      * @param initialTransmittedPowerdBm initial transmitted power to start the
662      *                                   estimation of radio source transmitted power
663      *                                   (expressed in dBm's).
664      * @throws IllegalArgumentException if quality scores is null, or length
665      *                                  of quality scores is less than required minimum.
666      */
667     public PROMedSRobustRssiRadioSourceEstimator3D(
668             final double[] qualityScores, final Point3D initialPosition, final Double initialTransmittedPowerdBm) {
669         super(initialPosition, initialTransmittedPowerdBm);
670         internalSetQualityScores(qualityScores);
671     }
672 
673     /**
674      * Constructor.
675      *
676      * @param qualityScores              quality scores corresponding to each provided
677      *                                   sample. The larger the score value the better
678      *                                   the quality of the sample.
679      * @param initialPosition            initial position to start the estimation of radio
680      *                                   source position.
681      * @param initialTransmittedPowerdBm initial transmitted power to start the
682      *                                   estimation of radio source transmitted power
683      *                                   (expressed in dBm's).
684      * @param listener                   in charge of attending events raised by this instance.
685      * @throws IllegalArgumentException if quality scores is null, or length
686      *                                  of quality scores is less than required minimum.
687      */
688     public PROMedSRobustRssiRadioSourceEstimator3D(
689             final double[] qualityScores, final Point3D initialPosition,
690             final Double initialTransmittedPowerdBm,
691             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
692         super(initialPosition, initialTransmittedPowerdBm, listener);
693         internalSetQualityScores(qualityScores);
694     }
695 
696     /**
697      * Constructor.
698      * Sets signal readings belonging to the same radio source.
699      *
700      * @param qualityScores              quality scores corresponding to each provided
701      *                                   sample. The larger the score value the better
702      *                                   the quality of the sample.
703      * @param readings                   signal readings containing to the same radio source.
704      * @param initialPosition            initial position to start the estimation of radio
705      *                                   source position.
706      * @param initialTransmittedPowerdBm initial transmitted power to start the
707      *                                   estimation of radio source transmitted power
708      *                                   (expressed in dBm's).
709      * @param listener                   listener in charge of attending events raised by this instance.
710      * @throws IllegalArgumentException if readings are not valid, quality scores
711      *                                  is null, or length of quality scores is less than required minimum.
712      */
713     public PROMedSRobustRssiRadioSourceEstimator3D(
714             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
715             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
716             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
717         super(readings, initialPosition, initialTransmittedPowerdBm, listener);
718         internalSetQualityScores(qualityScores);
719     }
720 
721     /**
722      * Constructor.
723      * Sets signal readings belonging to the same radio source.
724      *
725      * @param qualityScores              quality scores corresponding to each provided
726      *                                   sample. The larger the score value the better
727      *                                   the quality of the sample.
728      * @param readings                   signal readings belonging to the same radio source.
729      * @param initialPosition            initial position to start the estimation of radio
730      *                                   source position.
731      * @param initialTransmittedPowerdBm initial transmitted power to start the
732      *                                   estimation of radio source transmitted power
733      *                                   (expressed in dBm's).
734      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
735      * @throws IllegalArgumentException if readings are not valid, quality scores
736      *                                  is null, or length of quality scores is less than required minimum.
737      */
738     public PROMedSRobustRssiRadioSourceEstimator3D(
739             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
740             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
741             final double initialPathLossExponent) {
742         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
743         internalSetQualityScores(qualityScores);
744     }
745 
746     /**
747      * Constructor.
748      *
749      * @param qualityScores              quality scores corresponding to each provided
750      *                                   sample. The larger the score value the better
751      *                                   the quality of the sample.
752      * @param initialPosition            initial position to start the estimation of radio
753      *                                   source position.
754      * @param initialTransmittedPowerdBm initial transmitted power to start the
755      *                                   estimation of radio source transmitted power
756      *                                   (expressed in dBm's).
757      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
758      */
759     public PROMedSRobustRssiRadioSourceEstimator3D(
760             final double[] qualityScores, final Point3D initialPosition, final Double initialTransmittedPowerdBm,
761             final double initialPathLossExponent) {
762         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
763         internalSetQualityScores(qualityScores);
764     }
765 
766     /**
767      * Constructor.
768      *
769      * @param qualityScores              quality scores corresponding to each provided
770      *                                   sample. The larger the score value the better
771      *                                   the quality of the sample.
772      * @param initialPosition            initial position to start the estimation of radio
773      *                                   source position.
774      * @param initialTransmittedPowerdBm initial transmitted power to start the
775      *                                   estimation of radio source transmitted power
776      *                                   (expressed in dBm's).
777      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
778      * @param listener                   listener in charge of attending events raised by this instance.
779      */
780     public PROMedSRobustRssiRadioSourceEstimator3D(
781             final double[] qualityScores, final Point3D initialPosition, final Double initialTransmittedPowerdBm,
782             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
783         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
784         internalSetQualityScores(qualityScores);
785     }
786 
787     /**
788      * Constructor.
789      * Sets signal readings belonging to the same radio source.
790      *
791      * @param qualityScores              quality scores corresponding to each provided
792      *                                   sample. The larger the score value the better
793      *                                   the quality of the sample.
794      * @param readings                   signal readings belonging to the same radio source.
795      * @param initialPosition            initial position to start the estimation of radio
796      *                                   source position.
797      * @param initialTransmittedPowerdBm initial transmitted power to start the
798      *                                   estimation of radio source transmitted power
799      *                                   (expressed in dBm's).
800      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
801      * @param listener                   listener in charge of attending events raised by this instance.
802      * @throws IllegalArgumentException if readings are not valid, quality scores
803      *                                  is null, or length of quality scores is less than required minimum.
804      */
805     public PROMedSRobustRssiRadioSourceEstimator3D(
806             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
807             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
808             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
809         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
810         internalSetQualityScores(qualityScores);
811     }
812 
813     /**
814      * Returns threshold to be used to keep the algorithm iterating in case that
815      * best estimated threshold using median of residuals is not small enough.
816      * Once a solution is found that generates a threshold below this value, the
817      * algorithm will stop.
818      * The stop threshold can be used to prevent the LMedS algorithm to iterate
819      * too many times in cases where samples have a very similar accuracy.
820      * For instance, in cases where proportion of outliers is very small (close
821      * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
822      * iterate for a long time trying to find the best solution when indeed
823      * there is no need to do that if a reasonable threshold has already been
824      * reached.
825      * Because of this behaviour the stop threshold can be set to a value much
826      * lower than the one typically used in RANSAC, and yet the algorithm could
827      * still produce even smaller thresholds in estimated results.
828      *
829      * @return stop threshold to stop the algorithm prematurely when a certain
830      * accuracy has been reached.
831      */
832     public double getStopThreshold() {
833         return stopThreshold;
834     }
835 
836     /**
837      * Sets threshold to be used to keep the algorithm iterating in case that
838      * best estimated threshold using median of residuals is not small enough.
839      * Once a solution is found that generates a threshold below this value,
840      * the algorithm will stop.
841      * The stop threshold can be used to prevent the LMedS algorithm to iterate
842      * too many times in cases where samples have a very similar accuracy.
843      * For instance, in cases where proportion of outliers is very small (close
844      * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
845      * iterate for a long time trying to find the best solution when indeed
846      * there is no need to do that if a reasonable threshold has already been
847      * reached.
848      * Because of this behaviour the stop threshold can be set to a value much
849      * lower than the one typically used in RANSAC, and yet the algorithm could
850      * still produce even smaller thresholds in estimated results.
851      *
852      * @param stopThreshold stop threshold to stop the algorithm prematurely
853      *                      when a certain accuracy has been reached.
854      * @throws IllegalArgumentException if provided value is zero or negative.
855      * @throws LockedException          if this solver is locked.
856      */
857     public void setStopThreshold(final double stopThreshold) throws LockedException {
858         if (isLocked()) {
859             throw new LockedException();
860         }
861         if (stopThreshold <= MIN_STOP_THRESHOLD) {
862             throw new IllegalArgumentException();
863         }
864 
865         this.stopThreshold = stopThreshold;
866     }
867 
868     /**
869      * Returns quality scores corresponding to each pair of
870      * positions and distances (i.e. sample).
871      * The larger the score value the better the quality of the sample.
872      * This implementation always returns null.
873      * Subclasses using quality scores must implement proper behavior.
874      *
875      * @return quality scores corresponding to each sample.
876      */
877     @Override
878     public double[] getQualityScores() {
879         return qualityScores;
880     }
881 
882     /**
883      * Sets quality scores corresponding to each pair of positions and
884      * distances (i.e. sample).
885      * The larger the score value the better the quality of the sample.
886      * This implementation makes no action.
887      * Subclasses using quality scores must implement proper behaviour.
888      *
889      * @param qualityScores quality scores corresponding to each pair of
890      *                      matched points.
891      * @throws IllegalArgumentException if provided quality scores length
892      *                                  is smaller than minimum required samples.
893      * @throws LockedException          if robust solver is locked because an
894      *                                  estimation is already in progress.
895      */
896     @Override
897     public void setQualityScores(final double[] qualityScores) throws LockedException {
898         if (isLocked()) {
899             throw new LockedException();
900         }
901         internalSetQualityScores(qualityScores);
902     }
903 
904     /**
905      * Indicates whether solver is ready to find a solution.
906      *
907      * @return true if solver is ready, false otherwise.
908      */
909     @Override
910     public boolean isReady() {
911         return super.isReady() && qualityScores != null && qualityScores.length == readings.size();
912     }
913 
914     /**
915      * Robustly estimates position, transmitted power and path-loss exponent for a
916      * radio source.
917      *
918      * @throws LockedException          if instance is busy during estimation.
919      * @throws NotReadyException        if estimator is not ready.
920      * @throws RobustEstimatorException if estimation fails for any reason
921      *                                  (i.e. numerical instability, no solution available, etc).
922      */
923     @Override
924     public void estimate() throws LockedException, NotReadyException, RobustEstimatorException {
925         if (isLocked()) {
926             throw new LockedException();
927         }
928         if (!isReady()) {
929             throw new NotReadyException();
930         }
931 
932         final var innerEstimator = new PROMedSRobustEstimator<>(
933                 new PROMedSRobustEstimatorListener<Solution<Point3D>>() {
934 
935                     @Override
936                     public double[] getQualityScores() {
937                         return qualityScores;
938                     }
939 
940                     @Override
941                     public double getThreshold() {
942                         return stopThreshold;
943                     }
944 
945                     @Override
946                     public int getTotalSamples() {
947                         return readings.size();
948                     }
949 
950                     @Override
951                     public int getSubsetSize() {
952                         return Math.max(preliminarySubsetSize, getMinReadings());
953                     }
954 
955                     @Override
956                     public void estimatePreliminarSolutions(
957                             final int[] samplesIndices, final List<Solution<Point3D>> solutions) {
958                         solvePreliminarySolutions(samplesIndices, solutions);
959                     }
960 
961                     @Override
962                     public double computeResidual(final Solution<Point3D> currentEstimation, final int i) {
963                         return residual(currentEstimation, i);
964                     }
965 
966                     @Override
967                     public boolean isReady() {
968                         return PROMedSRobustRssiRadioSourceEstimator3D.this.isReady();
969                     }
970 
971                     @Override
972                     public void onEstimateStart(final RobustEstimator<Solution<Point3D>> estimator) {
973                         // no action needed
974                     }
975 
976                     @Override
977                     public void onEstimateEnd(final RobustEstimator<Solution<Point3D>> estimator) {
978                         // no action needed
979                     }
980 
981                     @Override
982                     public void onEstimateNextIteration(
983                             final RobustEstimator<Solution<Point3D>> estimator, final int iteration) {
984                         if (listener != null) {
985                             listener.onEstimateNextIteration(
986                                     PROMedSRobustRssiRadioSourceEstimator3D.this, iteration);
987                         }
988                     }
989 
990                     @Override
991                     public void onEstimateProgressChange(
992                             final RobustEstimator<Solution<Point3D>> estimator, final float progress) {
993                         if (listener != null) {
994                             listener.onEstimateProgressChange(
995                                     PROMedSRobustRssiRadioSourceEstimator3D.this, progress);
996                         }
997                     }
998                 });
999 
1000         try {
1001             locked = true;
1002 
1003             if (listener != null) {
1004                 listener.onEstimateStart(this);
1005             }
1006 
1007             inliersData = null;
1008 
1009             // inlier thresholds are disable to obtain a less restrictive amount of inliers
1010             innerEstimator.setUseInlierThresholds(false);
1011 
1012             innerEstimator.setConfidence(confidence);
1013             innerEstimator.setMaxIterations(maxIterations);
1014             innerEstimator.setProgressDelta(progressDelta);
1015             final var result = innerEstimator.estimate();
1016             inliersData = innerEstimator.getInliersData();
1017             attemptRefine(result);
1018 
1019             if (listener != null) {
1020                 listener.onEstimateEnd(this);
1021             }
1022 
1023         } catch (final com.irurueta.numerical.LockedException e) {
1024             throw new LockedException(e);
1025         } catch (final com.irurueta.numerical.NotReadyException e) {
1026             throw new NotReadyException(e);
1027         } finally {
1028             locked = false;
1029         }
1030     }
1031 
1032     /**
1033      * Returns method being used for robust estimation.
1034      *
1035      * @return method being used for robust estimation.
1036      */
1037     @Override
1038     public RobustEstimatorMethod getMethod() {
1039         return RobustEstimatorMethod.PROMEDS;
1040     }
1041 
1042     /**
1043      * Sets quality scores corresponding to each provided sample.
1044      * This method is used internally and does not check whether instance is
1045      * locked or not.
1046      *
1047      * @param qualityScores quality scores to be set.
1048      * @throws IllegalArgumentException if provided quality scores length
1049      *                                  is smaller than 3 samples.
1050      */
1051     private void internalSetQualityScores(final double[] qualityScores) {
1052         if (qualityScores == null || qualityScores.length < getMinReadings()) {
1053             throw new IllegalArgumentException();
1054         }
1055 
1056         this.qualityScores = qualityScores;
1057     }
1058 }