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