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.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 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 PROSAC
34   * algorithm and assuming that the access point 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 access points 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 PROSACRobustRssiRadioSourceEstimator3D<S extends RadioSource> extends RobustRssiRadioSourceEstimator3D<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 PROSACRobustRssiRadioSourceEstimator3D() {
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 PROSACRobustRssiRadioSourceEstimator3D(final List<? extends RssiReadingLocated<S, Point3D>> 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 PROSACRobustRssiRadioSourceEstimator3D(final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
148             final List<? extends RssiReadingLocated<S, Point3D>> readings,
149             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
163             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(
185             final Point3D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
200             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
201             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(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 PROSACRobustRssiRadioSourceEstimator3D(
227             final List<? extends RssiReadingLocated<S, Point3D>> 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 PROSACRobustRssiRadioSourceEstimator3D(
240             final Double initialTransmittedPowerdBm,
241             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
257             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Double initialTransmittedPowerdBm,
258             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
275             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(
290             final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(
305             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
306             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
324             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
325             final Double initialTransmittedPowerdBm,
326             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
344             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(
360             final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(
377             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
378             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
397             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
398             final Double initialTransmittedPowerdBm, final double initialPathLossExponent,
399             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(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 PROSACRobustRssiRadioSourceEstimator3D(
429             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> 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 PROSACRobustRssiRadioSourceEstimator3D(
445             final double[] qualityScores, final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
463             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
464             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
483             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
484             final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(final double[] qualityScores, final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(
516             final double[] qualityScores, final Point3D initialPosition,
517             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
537             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
538             final Point3D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 access point 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 PROSACRobustRssiRadioSourceEstimator3D(
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                   Wi-Fi 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 PROSACRobustRssiRadioSourceEstimator3D(
576             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> 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 PROSACRobustRssiRadioSourceEstimator3D(
596             final double[] qualityScores, final Double initialTransmittedPowerdBm,
597             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
618             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
619             final Double initialTransmittedPowerdBm,
620             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
642             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
643             final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(
663             final double[] qualityScores, final Point3D 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 PROSACRobustRssiRadioSourceEstimator3D(
684             final double[] qualityScores, final Point3D initialPosition, final Double initialTransmittedPowerdBm,
685             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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                   Wi-Fi 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 PROSACRobustRssiRadioSourceEstimator3D(
708             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
709             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
710             final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROSACRobustRssiRadioSourceEstimator3D(
733             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
734             final Point3D initialPosition, Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
735         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
736         internalSetQualityScores(qualityScores);
737     }
738 
739     /**
740      * Constructor.
741      *
742      * @param qualityScores              quality scores corresponding to each provided
743      *                                   sample. The larger the score value the better
744      *                                   the quality of the sample.
745      * @param initialPosition            initial position to start the estimation of radio
746      *                                   source position.
747      * @param initialTransmittedPowerdBm initial transmitted power to start the
748      *                                   estimation of radio source transmitted power
749      *                                   (expressed in dBm's).
750      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
751      */
752     public PROSACRobustRssiRadioSourceEstimator3D(
753             final double[] qualityScores, final Point3D initialPosition, final Double initialTransmittedPowerdBm,
754             final double initialPathLossExponent) {
755         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
756         internalSetQualityScores(qualityScores);
757     }
758 
759     /**
760      * Constructor.
761      *
762      * @param qualityScores              quality scores corresponding to each provided
763      *                                   sample. The larger the score value the better
764      *                                   the quality of the sample.
765      * @param initialPosition            initial position to start the estimation of radio
766      *                                   source position.
767      * @param initialTransmittedPowerdBm initial transmitted power to start the
768      *                                   estimation of radio source transmitted power
769      *                                   (expressed in dBm's).
770      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
771      * @param listener                   listener in charge of attending events raised by this instance.
772      */
773     public PROSACRobustRssiRadioSourceEstimator3D(
774             final double[] qualityScores, final Point3D initialPosition, final Double initialTransmittedPowerdBm,
775             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
776         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
777         internalSetQualityScores(qualityScores);
778     }
779 
780     /**
781      * Constructor.
782      * Sets Wi-Fi signal readings belonging to the same radio source.
783      *
784      * @param qualityScores              quality scores corresponding to each provided
785      *                                   sample. The larger the score value the better
786      *                                   the quality of the sample.
787      * @param readings                   Wi-Fi signal readings belonging to the same radio source.
788      * @param initialPosition            initial position to start the estimation of radio
789      *                                   source position.
790      * @param initialTransmittedPowerdBm initial transmitted power to start the
791      *                                   estimation of radio source transmitted power
792      *                                   (expressed in dBm's).
793      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
794      * @param listener                   listener in charge of attending events raised by this instance.
795      * @throws IllegalArgumentException if readings are not valid, quality scores
796      *                                  is null, or length of quality scores is less than required minimum.
797      */
798     public PROSACRobustRssiRadioSourceEstimator3D(
799             final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
800             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
801             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
802         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
803         internalSetQualityScores(qualityScores);
804     }
805 
806     /**
807      * Gets threshold to determine whether samples are inliers or not when testing possible solutions.
808      * The threshold refers to the amount of error on distance between estimated position and distances
809      * provided for each sample.
810      *
811      * @return threshold to determine whether samples are inliers or not.
812      */
813     public double getThreshold() {
814         return threshold;
815     }
816 
817     /**
818      * Sets threshold to determine whether samples are inliers or not when testing possible solutions.
819      * The threshold refers to the amount of error on distance between estimated position and distances
820      * provided for each sample.
821      *
822      * @param threshold threshold to determine whether samples are inliers or not.
823      * @throws IllegalArgumentException if provided value is equal or less than zero.
824      * @throws LockedException          if this solver is locked.
825      */
826     public void setThreshold(final double threshold) throws LockedException {
827         if (isLocked()) {
828             throw new LockedException();
829         }
830         if (threshold <= MIN_THRESHOLD) {
831             throw new IllegalArgumentException();
832         }
833         this.threshold = threshold;
834     }
835 
836     /**
837      * Returns quality scores corresponding to each pair of
838      * positions and distances (i.e. sample).
839      * The larger the score value the better the quality of the sample.
840      * This implementation always returns null.
841      * Subclasses using quality scores must implement proper behavior.
842      *
843      * @return quality scores corresponding to each sample.
844      */
845     @Override
846     public double[] getQualityScores() {
847         return qualityScores;
848     }
849 
850     /**
851      * Sets quality scores corresponding to each pair of positions and
852      * distances (i.e. sample).
853      * The larger the score value the better the quality of the sample.
854      * This implementation makes no action.
855      * Subclasses using quality scores must implement proper behaviour.
856      *
857      * @param qualityScores quality scores corresponding to each pair of
858      *                      matched points.
859      * @throws IllegalArgumentException if provided quality scores length
860      *                                  is smaller than minimum required samples.
861      * @throws LockedException          if robust solver is locked because an
862      *                                  estimation is already in progress.
863      */
864     @Override
865     public void setQualityScores(final double[] qualityScores) throws LockedException {
866         if (isLocked()) {
867             throw new LockedException();
868         }
869         internalSetQualityScores(qualityScores);
870     }
871 
872     /**
873      * Indicates whether solver is ready to find a solution.
874      *
875      * @return true if solver is ready, false otherwise.
876      */
877     @Override
878     public boolean isReady() {
879         return super.isReady() && qualityScores != null && qualityScores.length == readings.size();
880     }
881 
882     /**
883      * Indicates whether inliers must be computed and kept.
884      *
885      * @return true if inliers must be computed and kept, false if inliers
886      * only need to be computed but not kept.
887      */
888     public boolean isComputeAndKeepInliersEnabled() {
889         return computeAndKeepInliers;
890     }
891 
892     /**
893      * Specifies whether inliers must be computed and kept.
894      *
895      * @param computeAndKeepInliers true if inliers must be computed and kept,
896      *                              false if inliers only need to be computed but not kept.
897      * @throws LockedException if this solver is locked.
898      */
899     public void setComputeAndKeepInliersEnabled(final boolean computeAndKeepInliers) throws LockedException {
900         if (isLocked()) {
901             throw new LockedException();
902         }
903         this.computeAndKeepInliers = computeAndKeepInliers;
904     }
905 
906     /**
907      * Indicates whether residuals must be computed and kept.
908      *
909      * @return true if residuals must be computed and kept, false if residuals
910      * only need to be computed but not kept.
911      */
912     public boolean isComputeAndKeepResidualsEnabled() {
913         return computeAndKeepResiduals;
914     }
915 
916     /**
917      * Specifies whether residuals must be computed and kept.
918      *
919      * @param computeAndKeepResiduals true if residuals must be computed and kept,
920      *                                false if residuals only need to be computed but not kept.
921      * @throws LockedException if this solver is locked.
922      */
923     public void setComputeAndKeepResidualsEnabled(final boolean computeAndKeepResiduals) throws LockedException {
924         if (isLocked()) {
925             throw new LockedException();
926         }
927         this.computeAndKeepResiduals = computeAndKeepResiduals;
928     }
929 
930     /**
931      * Robustly estimates position, transmitted power and path-loss exponent for a
932      * radio source.
933      *
934      * @throws LockedException          if instance is busy during estimation.
935      * @throws NotReadyException        if estimator is not ready.
936      * @throws RobustEstimatorException if estimation fails for any reason
937      *                                  (i.e. numerical instability, no solution available, etc).
938      */
939     @Override
940     public void estimate() throws LockedException, NotReadyException, RobustEstimatorException {
941         if (isLocked()) {
942             throw new LockedException();
943         }
944         if (!isReady()) {
945             throw new NotReadyException();
946         }
947 
948         final var innerEstimator = new PROSACRobustEstimator<>(new PROSACRobustEstimatorListener<Solution<Point3D>>() {
949 
950             @Override
951             public double[] getQualityScores() {
952                 return qualityScores;
953             }
954 
955             @Override
956             public double getThreshold() {
957                 return threshold;
958             }
959 
960             @Override
961             public int getTotalSamples() {
962                 return readings.size();
963             }
964 
965             @Override
966             public int getSubsetSize() {
967                 return Math.max(preliminarySubsetSize, getMinReadings());
968             }
969 
970             @Override
971             public void estimatePreliminarSolutions(
972                     final int[] samplesIndices, final List<Solution<Point3D>> solutions) {
973                 solvePreliminarySolutions(samplesIndices, solutions);
974             }
975 
976             @Override
977             public double computeResidual(final Solution<Point3D> currentEstimation, final int i) {
978                 return residual(currentEstimation, i);
979             }
980 
981             @Override
982             public boolean isReady() {
983                 return PROSACRobustRssiRadioSourceEstimator3D.this.isReady();
984             }
985 
986             @Override
987             public void onEstimateStart(final RobustEstimator<Solution<Point3D>> estimator) {
988                 // no action needed
989             }
990 
991             @Override
992             public void onEstimateEnd(final RobustEstimator<Solution<Point3D>> estimator) {
993                 // no action needed
994             }
995 
996             @Override
997             public void onEstimateNextIteration(
998                     final RobustEstimator<Solution<Point3D>> estimator, final int iteration) {
999                 if (listener != null) {
1000                     listener.onEstimateNextIteration(
1001                             PROSACRobustRssiRadioSourceEstimator3D.this, iteration);
1002                 }
1003             }
1004 
1005             @Override
1006             public void onEstimateProgressChange(
1007                     final RobustEstimator<Solution<Point3D>> estimator, final float progress) {
1008                 if (listener != null) {
1009                     listener.onEstimateProgressChange(
1010                             PROSACRobustRssiRadioSourceEstimator3D.this, progress);
1011                 }
1012             }
1013         });
1014 
1015         try {
1016             locked = true;
1017 
1018             if (listener != null) {
1019                 listener.onEstimateStart(this);
1020             }
1021 
1022             inliersData = null;
1023             innerEstimator.setComputeAndKeepInliersEnabled(computeAndKeepInliers || refineResult);
1024             innerEstimator.setComputeAndKeepResidualsEnabled(computeAndKeepResiduals || refineResult);
1025             innerEstimator.setConfidence(confidence);
1026             innerEstimator.setMaxIterations(maxIterations);
1027             innerEstimator.setProgressDelta(progressDelta);
1028             final var result = innerEstimator.estimate();
1029             inliersData = innerEstimator.getInliersData();
1030             attemptRefine(result);
1031 
1032             if (listener != null) {
1033                 listener.onEstimateEnd(this);
1034             }
1035 
1036         } catch (final com.irurueta.numerical.LockedException e) {
1037             throw new LockedException(e);
1038         } catch (final com.irurueta.numerical.NotReadyException e) {
1039             throw new NotReadyException(e);
1040         } finally {
1041             locked = false;
1042         }
1043     }
1044 
1045     /**
1046      * Returns method being used for robust estimation.
1047      *
1048      * @return method being used for robust estimation.
1049      */
1050     @Override
1051     public RobustEstimatorMethod getMethod() {
1052         return RobustEstimatorMethod.PROSAC;
1053     }
1054 
1055     /**
1056      * Sets quality scores corresponding to each provided sample.
1057      * This method is used internally and does not check whether instance is
1058      * locked or not.
1059      *
1060      * @param qualityScores quality scores to be set.
1061      * @throws IllegalArgumentException if provided quality scores length
1062      *                                  is smaller than required minimum.
1063      */
1064     private void internalSetQualityScores(final double[] qualityScores) {
1065         if (qualityScores == null || qualityScores.length < getMinReadings()) {
1066             throw new IllegalArgumentException();
1067         }
1068 
1069         this.qualityScores = qualityScores;
1070     }
1071 }