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