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.RangingFingerprint;
23 import com.irurueta.navigation.indoor.RangingReading;
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 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 3D position of a given
33 * device by getting ranging readings at an unknown location of different radio sources
34 * whose 3D locations are known.
35 */
36 public class PROSACRobustRangingPositionEstimator3D extends RobustRangingPositionEstimator3D {
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 PROSACRobustRangingPositionEstimator3D() {
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 PROSACRobustRangingPositionEstimator3D(final List<? extends RadioSourceLocated<Point3D>> 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 PROSACRobustRangingPositionEstimator3D(
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 PROSACRobustRangingPositionEstimator3D(
96 final List<? extends RadioSourceLocated<Point3D>> 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 PROSACRobustRangingPositionEstimator3D(final RobustRangingPositionEstimatorListener<Point3D> 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 PROSACRobustRangingPositionEstimator3D(
124 final List<? extends RadioSourceLocated<Point3D>> sources,
125 final RobustRangingPositionEstimatorListener<Point3D> 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 PROSACRobustRangingPositionEstimator3D(
140 final RangingFingerprint<? extends RadioSource, ? extends RangingReading<?
141 extends RadioSource>> fingerprint,
142 final RobustRangingPositionEstimatorListener<Point3D> 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 PROSACRobustRangingPositionEstimator3D(
159 final List<? extends RadioSourceLocated<Point3D>> sources,
160 final RangingFingerprint<? extends RadioSource, ? extends RangingReading<?
161 extends RadioSource>> fingerprint,
162 final RobustRangingPositionEstimatorListener<Point3D> 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 PROSACRobustRangingPositionEstimator3D(
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 PROSACRobustRangingPositionEstimator3D(
205 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
206 final List<? extends RadioSourceLocated<Point3D>> 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
224 * at an unknown location for provided
225 * located radio sources.
226 * @throws IllegalArgumentException if provided fingerprint is null.
227 */
228 public PROSACRobustRangingPositionEstimator3D(
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
251 * at an unknown location for provided
252 * located 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 PROSACRobustRangingPositionEstimator3D(
257 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
258 final List<? extends RadioSourceLocated<Point3D>> 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 PROSACRobustRangingPositionEstimator3D(
280 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
281 final RobustRangingPositionEstimatorListener<Point3D> 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 PROSACRobustRangingPositionEstimator3D(
305 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
306 final List<? extends RadioSourceLocated<Point3D>> sources,
307 final RobustRangingPositionEstimatorListener<Point3D> 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 PROSACRobustRangingPositionEstimator3D(
331 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
332 final RangingFingerprint<? extends RadioSource, ? extends RangingReading<?
333 extends RadioSource>> fingerprint,
334 final RobustRangingPositionEstimatorListener<Point3D> 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 PROSACRobustRangingPositionEstimator3D(
361 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
362 final List<? extends RadioSourceLocated<Point3D>> sources,
363 final RangingFingerprint<? extends RadioSource, ? extends RangingReading<?
364 extends RadioSource>> fingerprint,
365 final RobustRangingPositionEstimatorListener<Point3D> 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 ((PROSACRobustLateration3DSolver) 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 ((PROSACRobustLateration3DSolver) 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 ((PROSACRobustLateration3DSolver) 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 ((PROSACRobustLateration3DSolver) 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 ((PROSACRobustLateration3DSolver) 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 ((PROSACRobustLateration3DSolver) 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 PROSACRobustLateration3DSolver(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 }