View Javadoc
1   /*
2    * Copyright (C) 2019 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.position;
17  
18  import com.irurueta.geometry.Point2D;
19  import com.irurueta.navigation.LockedException;
20  import com.irurueta.navigation.indoor.RadioSource;
21  import com.irurueta.navigation.indoor.RadioSourceLocated;
22  import com.irurueta.navigation.indoor.RangingAndRssiFingerprint;
23  import com.irurueta.navigation.indoor.RangingAndRssiReading;
24  import com.irurueta.navigation.lateration.PROSACRobustLateration2DSolver;
25  import com.irurueta.numerical.robust.RobustEstimatorMethod;
26  
27  import java.util.List;
28  
29  /**
30   * Robustly estimates 2D position using located radio sources and their
31   * ranging+RSSI readings at unknown locations and using PROSAC algorithm to discard
32   * outliers.
33   * This kind of estimator can be used to robustly determine the 2D position of a given
34   * device by getting ranging+RSSI readings at an unknown location of different radio
35   * sources whose 2D locations are known.
36   */
37  public class PROSACRobustRangingAndRssiPositionEstimator2D extends RobustRangingAndRssiPositionEstimator2D {
38  
39      /**
40       * Quality scores corresponding to each provided located radio source.
41       * The larger the score value the better the quality of the radio source.
42       */
43      private double[] sourceQualityScores;
44  
45      /**
46       * Quality scores corresponding to each reading within provided fingerprint.
47       * The larger the score value the better the quality of the reading.
48       */
49      private double[] fingerprintReadingsQualityScores;
50  
51      /**
52       * Constructor.
53       */
54      public PROSACRobustRangingAndRssiPositionEstimator2D() {
55          super();
56          init();
57      }
58  
59      /**
60       * Constructor.
61       *
62       * @param sources located radio sources used for lateration.
63       * @throws IllegalArgumentException if provided sources is null or the number of
64       *                                  provided sources is less than the required minimum.
65       */
66      public PROSACRobustRangingAndRssiPositionEstimator2D(final List<? extends RadioSourceLocated<Point2D>> sources) {
67          super();
68          init();
69          internalSetSources(sources);
70      }
71  
72      /**
73       * Constructor.
74       *
75       * @param fingerprint fingerprint containing ranging+RSSI readings at an unknown
76       *                    location for provided located radio sources.
77       * @throws IllegalArgumentException if provided fingerprint is null.
78       */
79      public PROSACRobustRangingAndRssiPositionEstimator2D(
80              final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
81                      extends RadioSource>> fingerprint) {
82          super();
83          init();
84          internalSetFingerprint(fingerprint);
85      }
86  
87      /**
88       * Constructor.
89       *
90       * @param sources     located radio sources used for lateration.
91       * @param fingerprint fingerprint containing ranging+RSSI readings at an unknown
92       *                    location for provided located radio sources.
93       * @throws IllegalArgumentException if either provided sources or fingerprint is null
94       *                                  or the number of provided sources is less than the required minimum.
95       */
96      public PROSACRobustRangingAndRssiPositionEstimator2D(
97              final List<? extends RadioSourceLocated<Point2D>> sources,
98              final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
99                      extends RadioSource>> fingerprint) {
100         super();
101         init();
102         internalSetSources(sources);
103         internalSetFingerprint(fingerprint);
104     }
105 
106     /**
107      * Constructor.
108      *
109      * @param listener listener in charge of handling events.
110      */
111     public PROSACRobustRangingAndRssiPositionEstimator2D(
112             final RobustRangingAndRssiPositionEstimatorListener<Point2D> listener) {
113         super(listener);
114         init();
115     }
116 
117     /**
118      * Constructor.
119      *
120      * @param sources  located radio sources used for lateration.
121      * @param listener listener in charge of handling events.
122      * @throws IllegalArgumentException if provided sources is null or the number of
123      *                                  provided sources is less than the required minimum.
124      */
125     public PROSACRobustRangingAndRssiPositionEstimator2D(
126             final List<? extends RadioSourceLocated<Point2D>> sources,
127             final RobustRangingAndRssiPositionEstimatorListener<Point2D> listener) {
128         super(listener);
129         init();
130         internalSetSources(sources);
131     }
132 
133     /**
134      * Constructor.
135      *
136      * @param fingerprint fingerprint containing ranging+RSSI readings at an unknown
137      *                    location for provided location radio sources.
138      * @param listener    listener in charge of handling events.
139      * @throws IllegalArgumentException if provided fingerprint is null.
140      */
141     public PROSACRobustRangingAndRssiPositionEstimator2D(
142             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
143                     extends RadioSource>> fingerprint,
144             final RobustRangingAndRssiPositionEstimatorListener<Point2D> listener) {
145         super(listener);
146         init();
147         internalSetFingerprint(fingerprint);
148     }
149 
150     /**
151      * Constructor.
152      *
153      * @param sources     located radio sources used for lateration.
154      * @param fingerprint fingerprint containing ranging+RSSI readings at an unknown
155      *                    location for provided located radio sources.
156      * @param listener    listener in charge of handling events.
157      * @throws IllegalArgumentException if either provided sources or fingerprint is
158      *                                  null or the number of provided sources is less than the required minimum.
159      */
160     public PROSACRobustRangingAndRssiPositionEstimator2D(
161             final List<? extends RadioSourceLocated<Point2D>> sources,
162             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
163                     extends RadioSource>> fingerprint,
164             final RobustRangingAndRssiPositionEstimatorListener<Point2D> listener) {
165         super(listener);
166         init();
167         internalSetSources(sources);
168         internalSetFingerprint(fingerprint);
169     }
170 
171     /**
172      * Constructor.
173      *
174      * @param sourceQualityScores             quality scores corresponding to
175      *                                        each provided located radio source.
176      *                                        The larger the score value the better
177      *                                        the quality of the radio source.
178      * @param fingerprintReadingQualityScores quality scores corresponding to readings
179      *                                        within provided fingerprint. The larger
180      *                                        the score the better the quality of the
181      *                                        reading.
182      */
183     public PROSACRobustRangingAndRssiPositionEstimator2D(
184             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores) {
185         this();
186         internalSetSourceQualityScores(sourceQualityScores);
187         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
188     }
189 
190     /**
191      * Constructor.
192      *
193      * @param sourceQualityScores             quality scores corresponding to
194      *                                        each provided located radio source.
195      *                                        The larger the score value the better
196      *                                        the quality of the radio source.
197      * @param fingerprintReadingQualityScores quality scores corresponding to readings
198      *                                        within provided fingerprint. The larger
199      *                                        the score the better the quality of the
200      *                                        reading.
201      * @param sources                         located radio sources used for
202      *                                        lateration.
203      * @throws IllegalArgumentException if provided sources is null or the number of
204      *                                  provided sources is less than the required minimum.
205      */
206     public PROSACRobustRangingAndRssiPositionEstimator2D(
207             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
208             final List<? extends RadioSourceLocated<Point2D>> sources) {
209         this(sources);
210         internalSetSourceQualityScores(sourceQualityScores);
211         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
212     }
213 
214     /**
215      * Constructor.
216      *
217      * @param sourceQualityScores             quality scores corresponding to
218      *                                        each provided located radio source.
219      *                                        The larger the score value the better
220      *                                        the quality of the radio source.
221      * @param fingerprintReadingQualityScores quality scores corresponding to readings
222      *                                        within provided fingerprint. The larger
223      *                                        the score the better the quality of the
224      *                                        reading.
225      * @param fingerprint                     fingerprint containing ranging+RSSI readings
226      *                                        at an unknown location for provided located
227      *                                        radio sources.
228      * @throws IllegalArgumentException if provided fingerprint is null.
229      */
230     public PROSACRobustRangingAndRssiPositionEstimator2D(
231             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
232             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
233                     extends RadioSource>> fingerprint) {
234         this(fingerprint);
235         internalSetSourceQualityScores(sourceQualityScores);
236         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
237     }
238 
239     /**
240      * Constructor.
241      *
242      * @param sourceQualityScores             quality scores corresponding to
243      *                                        each provided located radio source.
244      *                                        The larger the score value the better
245      *                                        the quality of the radio source.
246      * @param fingerprintReadingQualityScores quality scores corresponding to readings
247      *                                        within provided fingerprint. The larger
248      *                                        the score the better the quality of the
249      *                                        reading.
250      * @param sources                         located radio sources used for
251      *                                        lateration.
252      * @param fingerprint                     fingerprint containing ranging+RSSI
253      *                                        readings at an unknown location for
254      *                                        provided located radio sources.
255      * @throws IllegalArgumentException if either provided sources or fingerprint is null
256      *                                  or the number of provided sources is less than the required minimum.
257      */
258     public PROSACRobustRangingAndRssiPositionEstimator2D(
259             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
260             final List<? extends RadioSourceLocated<Point2D>> sources,
261             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
262                     extends RadioSource>> fingerprint) {
263         this(sources, fingerprint);
264         internalSetSourceQualityScores(sourceQualityScores);
265         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
266     }
267 
268     /**
269      * Constructor.
270      *
271      * @param sourceQualityScores             quality scores corresponding to
272      *                                        each provided located radio source.
273      *                                        The larger the score value the better
274      *                                        the quality of the radio source.
275      * @param fingerprintReadingQualityScores quality scores corresponding to readings
276      *                                        within provided fingerprint. The larger
277      *                                        the score the better the quality of the
278      *                                        reading.
279      * @param listener                        listener in charge of handling events.
280      */
281     public PROSACRobustRangingAndRssiPositionEstimator2D(
282             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
283             final RobustRangingAndRssiPositionEstimatorListener<Point2D> listener) {
284         this(listener);
285         internalSetSourceQualityScores(sourceQualityScores);
286         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
287     }
288 
289     /**
290      * Constructor.
291      *
292      * @param sourceQualityScores             quality scores corresponding to
293      *                                        each provided located radio source.
294      *                                        The larger the score value the better
295      *                                        the quality of the radio source.
296      * @param fingerprintReadingQualityScores quality scores corresponding to readings
297      *                                        within provided fingerprint. The larger
298      *                                        the score the better the quality of the
299      *                                        reading.
300      * @param sources                         located radio sources used for
301      *                                        lateration.
302      * @param listener                        listener in charge of handling events.
303      * @throws IllegalArgumentException if provided sources is null or the number of
304      *                                  provided sources is less than the required minimum.
305      */
306     public PROSACRobustRangingAndRssiPositionEstimator2D(
307             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
308             final List<? extends RadioSourceLocated<Point2D>> sources,
309             final RobustRangingAndRssiPositionEstimatorListener<Point2D> listener) {
310         this(sources, listener);
311         internalSetSourceQualityScores(sourceQualityScores);
312         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
313     }
314 
315     /**
316      * Constructor.
317      *
318      * @param sourceQualityScores             quality scores corresponding to
319      *                                        each provided located radio source.
320      *                                        The larger the score value the better
321      *                                        the quality of the radio source.
322      * @param fingerprintReadingQualityScores quality scores corresponding to readings
323      *                                        within provided fingerprint. The larger
324      *                                        the score the better the quality of the
325      *                                        reading.
326      * @param fingerprint                     fingerprint containing ranging+RSSI
327      *                                        readings at an unknown location for
328      *                                        provided location radio sources.
329      * @param listener                        listener in charge of handling events.
330      * @throws IllegalArgumentException if provided fingerprint is null.
331      */
332     public PROSACRobustRangingAndRssiPositionEstimator2D(
333             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
334             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
335                     extends RadioSource>> fingerprint,
336             final RobustRangingAndRssiPositionEstimatorListener<Point2D> listener) {
337         this(fingerprint, listener);
338         internalSetSourceQualityScores(sourceQualityScores);
339         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
340     }
341 
342     /**
343      * Constructor.
344      *
345      * @param sourceQualityScores             quality scores corresponding to
346      *                                        each provided located radio source.
347      *                                        The larger the score value the better
348      *                                        the quality of the radio source.
349      * @param fingerprintReadingQualityScores quality scores corresponding to readings
350      *                                        within provided fingerprint. The larger
351      *                                        the score the better the quality of the
352      *                                        reading.
353      * @param sources                         located radio sources used for
354      *                                        lateration.
355      * @param fingerprint                     fingerprint containing ranging+RSSI
356      *                                        readings at an unknown location for
357      *                                        provided located radio sources.
358      * @param listener                        listener in charge of handling events.
359      * @throws IllegalArgumentException if either provided sources or fingerprint is
360      *                                  null or the number of provided sources is less than the required minimum.
361      */
362     public PROSACRobustRangingAndRssiPositionEstimator2D(
363             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
364             final List<? extends RadioSourceLocated<Point2D>> sources,
365             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
366                     extends RadioSource>> fingerprint,
367             final RobustRangingAndRssiPositionEstimatorListener<Point2D> listener) {
368         this(sources, fingerprint, listener);
369         internalSetSourceQualityScores(sourceQualityScores);
370         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
371     }
372 
373     /**
374      * Returns quality scores corresponding to each radio source.
375      * The larger the score value the better the quality of the sample.
376      *
377      * @return quality scores corresponding to each radio source.
378      */
379     @Override
380     public double[] getSourceQualityScores() {
381         return sourceQualityScores;
382     }
383 
384     /**
385      * Sets quality scores corresponding to each radio source.
386      * The larger the score value the better the quality of the radio source.
387      *
388      * @param sourceQualityScores quality scores corresponding to each radio source.
389      * @throws LockedException          if this instance is locked.
390      * @throws IllegalArgumentException if provided quality scores length is smaller
391      *                                  than minimum required samples.
392      */
393     @Override
394     public void setSourceQualityScores(final double[] sourceQualityScores) throws LockedException {
395         if (isLocked()) {
396             throw new LockedException();
397         }
398         internalSetSourceQualityScores(sourceQualityScores);
399     }
400 
401     /**
402      * Gets quality scores corresponding to each reading within provided fingerprint.
403      * The larger the score value the better the quality of the reading.
404      * This implementation always returns null.
405      * Subclasses using quality scores must implement proper behavior.
406      *
407      * @return quality scores corresponding to each reading within provided
408      * fingerprint.
409      */
410     @Override
411     public double[] getFingerprintReadingsQualityScores() {
412         return fingerprintReadingsQualityScores;
413     }
414 
415     /**
416      * Sets quality scores corresponding to each reading within provided fingerprint.
417      * The larger the score value the better the quality of the reading.
418      * This implementation makes no action.
419      * Subclasses using quality scores must implement proper behavior.
420      *
421      * @param fingerprintReadingsQualityScores quality scores corresponding to each
422      *                                         reading within provided fingerprint.
423      * @throws LockedException          if this instance is locked.
424      * @throws IllegalArgumentException if provided quality scores length is smaller
425      *                                  than minimum required samples.
426      */
427     @Override
428     public void setFingerprintReadingsQualityScores(final double[] fingerprintReadingsQualityScores)
429             throws LockedException {
430         if (isLocked()) {
431             throw new LockedException();
432         }
433         internalSetFingerprintReadingsQualityScores(fingerprintReadingsQualityScores);
434     }
435 
436     /**
437      * Gets threshold to determine whether samples are inliers or not when testing possible solutions.
438      * The threshold refers to the amount of error on distance between estimated position and distances
439      * provided for each sample.
440      *
441      * @return threshold to determine whether samples are inliers or not.
442      */
443     public double getThreshold() {
444         return ((PROSACRobustLateration2DSolver) laterationSolver).getThreshold();
445     }
446 
447     /**
448      * Sets threshold to determine whether samples are inliers or not when testing possible solutions.
449      * The threshold refers to the amount of error on distance between estimated position and distances
450      * provided for each sample.
451      *
452      * @param threshold threshold to determine whether samples are inliers or not.
453      * @throws IllegalArgumentException if provided value is equal or less than zero.
454      * @throws LockedException          if this solver is locked.
455      */
456     public void setThreshold(final double threshold) throws LockedException {
457         ((PROSACRobustLateration2DSolver) laterationSolver).setThreshold(threshold);
458     }
459 
460     /**
461      * Indicates whether inliers must be computed and kept.
462      *
463      * @return true if inliers must be computed and kept, false if inliers
464      * only need to be computed but not kept.
465      */
466     public boolean isComputeAndKeepInliersEnabled() {
467         return ((PROSACRobustLateration2DSolver) laterationSolver).isComputeAndKeepInliersEnabled();
468     }
469 
470     /**
471      * Specifies whether inliers must be computed and kept.
472      *
473      * @param computeAndKeepInliers true if inliers must be computed and kept,
474      *                              false if inliers only need to be computed but not kept.
475      * @throws LockedException if this solver is locked.
476      */
477     public void setComputeAndKeepInliersEnabled(final boolean computeAndKeepInliers) throws LockedException {
478         ((PROSACRobustLateration2DSolver) laterationSolver).setComputeAndKeepInliersEnabled(computeAndKeepInliers);
479     }
480 
481     /**
482      * Indicates whether residuals must be computed and kept.
483      *
484      * @return true if residuals must be computed and kept, false if residuals
485      * only need to be computed but not kept.
486      */
487     public boolean isComputeAndKeepResiduals() {
488         return ((PROSACRobustLateration2DSolver) laterationSolver).isComputeAndKeepResiduals();
489     }
490 
491     /**
492      * Specifies whether residuals must be computed and kept.
493      *
494      * @param computeAndKeepResiduals true if residuals must be computed and kept,
495      *                                false if residuals only need to be computed but not kept.
496      * @throws LockedException if this solver is locked.
497      */
498     public void setComputeAndKeepResidualsEnabled(final boolean computeAndKeepResiduals) throws LockedException {
499         ((PROSACRobustLateration2DSolver) laterationSolver).setComputeAndKeepResidualsEnabled(computeAndKeepResiduals);
500     }
501 
502     /**
503      * Returns method being used for robust estimation.
504      *
505      * @return method being used for robust estimation.
506      */
507     @Override
508     public RobustEstimatorMethod getMethod() {
509         return RobustEstimatorMethod.PROSAC;
510     }
511 
512     /**
513      * Initializes robust lateration solver.
514      */
515     private void init() {
516         laterationSolver = new PROSACRobustLateration2DSolver(trilaterationSolverListener);
517     }
518 
519     /**
520      * Sets quality scores corresponding to each provided located radio source.
521      * This method is used internally and does not check whether instance is
522      * locked or not.
523      *
524      * @param sourceQualityScores quality scores to be set.
525      * @throws IllegalArgumentException if provided quality scores length
526      *                                  is smaller than 3 samples.
527      */
528     private void internalSetSourceQualityScores(final double[] sourceQualityScores) {
529         if (sourceQualityScores == null || sourceQualityScores.length < getMinRequiredSources()) {
530             throw new IllegalArgumentException();
531         }
532 
533         this.sourceQualityScores = sourceQualityScores;
534 
535         buildPositionsDistancesDistanceStandardDeviationsAndQualityScores();
536     }
537 
538     /**
539      * Sets quality scores corresponding to each provided reading within provided
540      * fingerprint.
541      * This method is used internally and does not check whether instance is locked
542      * or not.
543      *
544      * @param fingerprintReadingsQualityScores quality scores to be set.
545      * @throws IllegalArgumentException if provided quality scores length is
546      *                                  smaller than 3 samples.
547      */
548     private void internalSetFingerprintReadingsQualityScores(final double[] fingerprintReadingsQualityScores) {
549         if (fingerprintReadingsQualityScores == null
550                 || fingerprintReadingsQualityScores.length < getMinRequiredSources()) {
551             throw new IllegalArgumentException();
552         }
553 
554         this.fingerprintReadingsQualityScores = fingerprintReadingsQualityScores;
555 
556         buildPositionsDistancesDistanceStandardDeviationsAndQualityScores();
557     }
558 }