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