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.algebra.Matrix;
19 import com.irurueta.geometry.Point;
20 import com.irurueta.navigation.LockedException;
21 import com.irurueta.navigation.NotReadyException;
22 import com.irurueta.navigation.indoor.Fingerprint;
23 import com.irurueta.navigation.indoor.RadioSource;
24 import com.irurueta.navigation.indoor.RadioSourceLocated;
25 import com.irurueta.navigation.indoor.RangingAndRssiReading;
26 import com.irurueta.navigation.indoor.RangingFingerprint;
27 import com.irurueta.navigation.indoor.RangingReading;
28 import com.irurueta.navigation.indoor.Reading;
29 import com.irurueta.navigation.indoor.RssiFingerprint;
30 import com.irurueta.navigation.indoor.RssiReading;
31 import com.irurueta.numerical.robust.InliersData;
32 import com.irurueta.numerical.robust.RobustEstimatorException;
33 import com.irurueta.numerical.robust.RobustEstimatorMethod;
34
35 import java.util.ArrayList;
36 import java.util.List;
37
38 /**
39 * Base class for robust position estimation, using RSSI readings first to obtain
40 * an initial coarse position estimation, and then ranging readings to refine such
41 * estimation.
42 * <p>
43 * This implementation is like SequentialRobustRangingAndRssiPositionEstimator but
44 * allows mixing different kinds of readings (ranging, RSSI or ranging+RSSI).
45 *
46 * @param <P> a {@link Point} type.
47 */
48 @SuppressWarnings("DuplicatedCode")
49 public abstract class SequentialRobustMixedPositionEstimator<P extends Point<?>> {
50
51 /**
52 * Default robust estimator method for robust position estimation using ranging
53 * data when no robust method is provided.
54 */
55 public static final RobustEstimatorMethod DEFAULT_RANGING_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
56
57 /**
58 * Default robust method for coarse robust position estimation using RSSI
59 * data when no robust method is provided.
60 */
61 public static final RobustEstimatorMethod DEFAULT_RSSI_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
62
63 /**
64 * Indicates that by default located radio source position covariance is taken
65 * into account (if available) to determine distance standard deviation for ranging
66 * measurements.
67 */
68 public static final boolean DEFAULT_USE_RANGING_RADIO_SOURCE_POSITION_COVARIANCE = true;
69
70 /**
71 * Indicates that by default located radio source position covariance is taken
72 * into account (if available) to determine distance standard deviation for RSSI
73 * measurements.
74 */
75 public static final boolean DEFAULT_USE_RSSI_RADIO_SOURCE_POSITION_COVARIANCE = true;
76
77 /**
78 * Indicates that by default readings are distributed evenly among radio sources
79 * taking into account quality scores of both radio sources and ranging readings.
80 */
81 public static final boolean DEFAULT_EVENLY_DISTRIBUTE_RANGING_READINGS = true;
82
83 /**
84 * Indicates that by default readings are distributed evenly among radio sources
85 * taking into account quality scores of both radio sources and RSSI readings.
86 */
87 public static final boolean DEFAULT_EVENLY_DISTRIBUTE_RSSI_READINGS = true;
88
89 /**
90 * Distance standard deviation assumed for provided distances as a fallback when
91 * none can be determined.
92 */
93 public static final double FALLBACK_DISTANCE_STANDARD_DEVIATION =
94 RobustPositionEstimator.FALLBACK_DISTANCE_STANDARD_DEVIATION;
95
96 /**
97 * Default amount of progress variation before notifying a change in estimation progress.
98 * By default, this is set to 5%.
99 */
100 public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
101
102 /**
103 * Minimum allowed value for progress delta.
104 */
105 public static final float MIN_PROGRESS_DELTA = 0.0f;
106
107 /**
108 * Maximum allowed value for progress delta.
109 */
110 public static final float MAX_PROGRESS_DELTA = 1.0f;
111
112 /**
113 * Constant defining default confidence of the estimated result, which is
114 * 99%. This means that with a probability of 99% estimation will be
115 * accurate because chosen sub-samples will be inliers.
116 */
117 public static final double DEFAULT_CONFIDENCE = 0.99;
118
119 /**
120 * Default maximum allowed number of iterations.
121 */
122 public static final int DEFAULT_MAX_ITERATIONS = 5000;
123
124 /**
125 * Minimum allowed confidence value.
126 */
127 public static final double MIN_CONFIDENCE = 0.0;
128
129 /**
130 * Maximum allowed confidence value.
131 */
132 public static final double MAX_CONFIDENCE = 1.0;
133
134 /**
135 * Minimum allowed number of iterations.
136 */
137 public static final int MIN_ITERATIONS = 1;
138
139 /**
140 * Indicates that result is refined by default using all found inliers.
141 */
142 public static final boolean DEFAULT_REFINE_RESULT = true;
143
144 /**
145 * Indicates that covariance is kept by default after refining result.
146 */
147 public static final boolean DEFAULT_KEEP_COVARIANCE = true;
148
149 /**
150 * Indicates that by default a linear solver is used for preliminary solution
151 * estimation using ranging measurements.
152 * The result obtained on each preliminary solution might be later refined.
153 */
154 public static final boolean DEFAULT_USE_RANGING_LINEAR_SOLVER = true;
155
156 /**
157 * Indicates that by default a linear solver is used for preliminary solution
158 * estimation using RSSI measurements.
159 * The result obtained on each preliminary solution might be later refined.
160 */
161 public static final boolean DEFAULT_USE_RSSI_LINEAR_SOLVER = true;
162
163 /**
164 * Indicates that by default an homogeneous linear solver is used either to
165 * estimate preliminary solutions or an initial solution for preliminary solutions
166 * that will be later refined on the ranging fine estimation.
167 */
168 public static final boolean DEFAULT_USE_RANGING_HOMOGENEOUS_LINEAR_SOLVER = false;
169
170 /**
171 * Indicates that by default an homogeneous linear solver is used either to
172 * estimate preliminary solutions or an initial solution for preliminary solutions
173 * that will be later refined on the RSSI coarse estimation.
174 */
175 public static final boolean DEFAULT_USE_RSSI_HOMOGENEOUS_LINEAR_SOLVER = false;
176
177 /**
178 * Indicates that by default preliminary ranging solutions are refined.
179 */
180 public static final boolean DEFAULT_REFINE_RANGING_PRELIMINARY_SOLUTIONS = true;
181
182 /**
183 * Indicates that by default preliminary RSSI solutions are refined.
184 */
185 public static final boolean DEFAULT_REFINE_RSSI_PRELIMINARY_SOLUTIONS = true;
186
187 /**
188 * Internal robust estimator for position estimation using ranging readings.
189 */
190 protected RobustRangingPositionEstimator<P> rangingEstimator;
191
192 /**
193 * Internal robust estimator for coarse position estimation using RSSI readings.
194 */
195 protected RobustRssiPositionEstimator<P> rssiEstimator;
196
197 /**
198 * Robust method used for robust position estimation using ranging data.
199 */
200 protected RobustEstimatorMethod rangingRobustMethod = DEFAULT_RANGING_ROBUST_METHOD;
201
202 /**
203 * Robust method used for coarse robust position estimation using RSSI data.
204 */
205 protected RobustEstimatorMethod rssiRobustMethod = DEFAULT_RSSI_ROBUST_METHOD;
206
207 /**
208 * Size of subsets to be checked during robust estimation.
209 */
210 protected int rangingPreliminarySubsetSize;
211
212 /**
213 * Size of subsets to be checked during RSSI robust estimation.
214 */
215 protected int rssiPreliminarySubsetSize;
216
217 /**
218 * Threshold to determine when samples are inliers or not used during ranging
219 * position estimation.
220 * If not defined, default threshold will be used.
221 */
222 protected Double rangingThreshold;
223
224 /**
225 * Threshold to determine when samples are inliers or not used during RSSI
226 * position estimation.
227 * If not defined, default threshold will be used.
228 */
229 protected Double rssiThreshold;
230
231 /**
232 * Indicates whether located radio source position covariance is taken into account
233 * (if available) to determine distance standard deviation for ranging measurements.
234 */
235 private boolean useRangingRadioSourcePositionCovariance = DEFAULT_USE_RANGING_RADIO_SOURCE_POSITION_COVARIANCE;
236
237 /**
238 * Indicates whether located radio source position covariance is taken into account
239 * (if available) to determine distance standard deviation for RSSI measurements.
240 */
241 private boolean useRssiRadioSourcePositionCovariance = DEFAULT_USE_RSSI_RADIO_SOURCE_POSITION_COVARIANCE;
242
243 /**
244 * Indicates whether ranging readings are evenly distributed among radio sources
245 * taking into account quality scores of both radio sources and ranging readings.
246 */
247 private boolean evenlyDistributeRangingReadings = DEFAULT_EVENLY_DISTRIBUTE_RANGING_READINGS;
248
249 /**
250 * Indicates whether RSSI readings are evenly distributed among radio sources
251 * taking into account quality scores of both radio sources and RSSI readings.
252 */
253 private boolean evenlyDistributeRssiReadings = DEFAULT_EVENLY_DISTRIBUTE_RSSI_READINGS;
254
255 /**
256 * Distance standard deviation fallback value to use when none can be determined
257 * from provided RSSI measurements.
258 */
259 private double rssiFallbackDistanceStandardDeviation = FALLBACK_DISTANCE_STANDARD_DEVIATION;
260
261 /**
262 * Distance standard deviation fallback value to use when none can be determined
263 * from provided ranging measurements.
264 */
265 private double rangingFallbackDistanceStandardDeviation =
266 FALLBACK_DISTANCE_STANDARD_DEVIATION;
267
268 /**
269 * Amount of progress variation before notifying a progress change during
270 * estimation.
271 */
272 private float progressDelta = DEFAULT_PROGRESS_DELTA;
273
274 /**
275 * Amount of confidence expressed as a value between 0.0 and 1.0 (which is
276 * equivalent to 100%) for robust position estimation on ranging data. The amount
277 * of confidence indicates the probability that the estimated result is correct.
278 * Usually this value will be close to 1.0, but not exactly 1.0.
279 */
280 private double rangingConfidence = DEFAULT_CONFIDENCE;
281
282 /**
283 * Amount of confidence expressed as a value between 0.0 and 1.0 (which is
284 * equivalent to 100%) for robust position estimation on RSSI data. The amount
285 * of confidence indicates the probability that the estimated result is correct.
286 * Usually this value will be close to 1.0, but not exactly 1.0.
287 */
288 private double rssiConfidence = DEFAULT_CONFIDENCE;
289
290 /**
291 * Maximum allowed number of iterations for robust ranging position estimation.
292 * When the maximum number of iterations is exceeded, an approximate result
293 * might be available for retrieval.
294 */
295 private int rangingMaxIterations = DEFAULT_MAX_ITERATIONS;
296
297 /**
298 * Maximum allowed number of iterations for robust RSSI position estimation.
299 * When the maximum number of iterations is exceeded, an approximate result
300 * might be available for retrieval.
301 */
302 private int rssiMaxIterations = DEFAULT_MAX_ITERATIONS;
303
304 /**
305 * Indicates whether result is refined using all found inliers.
306 */
307 private boolean refineResult = DEFAULT_REFINE_RESULT;
308
309 /**
310 * Indicates that covariance is kept after refining result.
311 */
312 private boolean keepCovariance = DEFAULT_KEEP_COVARIANCE;
313
314 /**
315 * Indicates that a linear solver is used for preliminary solution estimation
316 * using ranging measurements.
317 * The result obtained on each preliminary solution might be later refined.
318 */
319 private boolean useRangingLinearSolver = DEFAULT_USE_RANGING_LINEAR_SOLVER;
320
321 /**
322 * Indicates that a linear solver is used for preliminary solution estimation
323 * using RSSI measurements.
324 * The result obtained on each preliminary solution might be later refined.
325 */
326 private boolean useRssiLinearSolver = DEFAULT_USE_RSSI_LINEAR_SOLVER;
327
328 /**
329 * Indicates whether an homogeneous linear solver is used either to estimate
330 * preliminary solutions or an initial solution for preliminary solutions that
331 * will be later refined on the ranging fine estimation.
332 */
333 private boolean useRangingHomogeneousLinearSolver = DEFAULT_USE_RANGING_HOMOGENEOUS_LINEAR_SOLVER;
334
335 /**
336 * Indicates whether an homogeneous linear solver is used either to estimate
337 * preliminary solutions or an initial solution for preliminary solutions that
338 * will be later refined on the RSSI coarse estimation.
339 */
340 private boolean useRssiHomogeneousLinearSolver = DEFAULT_USE_RSSI_HOMOGENEOUS_LINEAR_SOLVER;
341
342 /**
343 * Indicates whether preliminary ranging solutions are refined.
344 */
345 private boolean refineRangingPreliminarySolutions = DEFAULT_REFINE_RANGING_PRELIMINARY_SOLUTIONS;
346
347 /**
348 * Indicates whether preliminary RSSI solutions are refined.
349 */
350 private boolean refineRssiPreliminarySolutions = DEFAULT_REFINE_RSSI_PRELIMINARY_SOLUTIONS;
351
352 /**
353 * Listener in charge of handling events.
354 */
355 private SequentialRobustMixedPositionEstimatorListener<P> listener;
356
357 /**
358 * Located radio sources used for lateration.
359 */
360 private List<? extends RadioSourceLocated<P>> sources;
361
362 /**
363 * Fingerprint containing readings at an unknown location for provided located
364 * radio sources.
365 */
366 private Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint;
367
368 /**
369 * Quality scores corresponding to each provided located radio source.
370 * The larger the score value the better the quality of the radio source.
371 */
372 private double[] sourceQualityScores;
373
374 /**
375 * Quality scores corresponding to each reading with provided fingerprint.
376 * The larger the score value the better the quality of the reading.
377 */
378 private double[] fingerprintReadingsQualityScores;
379
380 /**
381 * An initial position to start the estimation from. This can be useful if we only
382 * intend to refine a previously known estimation.
383 */
384 private P initialPosition;
385
386 /**
387 * Indicates if this instance is locked because estimation is being executed.
388 */
389 private boolean locked;
390
391 /**
392 * Indicates whether ranging estimation must be available or not using
393 * provided fingerprint readings.
394 */
395 private boolean rangingEstimatorAvailable;
396
397 /**
398 * Indicates whether RSSI estimation must be available or not using
399 * provided fingerprint readings.
400 */
401 private boolean rssiEstimatorAvailable;
402
403 /**
404 * Number of ranging readings found on provided fingerprint.
405 */
406 private int numRangingReadings;
407
408 /**
409 * Number of RSSI readings found on provided fingerprint.
410 */
411 private int numRssiReadings;
412
413 /**
414 * Constructor.
415 */
416 protected SequentialRobustMixedPositionEstimator() {
417 }
418
419 /**
420 * Constructor.
421 *
422 * @param sources located radio sources used for lateration.
423 * @throws IllegalArgumentException if provided sources is null or the number of
424 * provided sources is less than the required minimum.
425 */
426 protected SequentialRobustMixedPositionEstimator(final List<? extends RadioSourceLocated<P>> sources) {
427 internalSetSources(sources);
428 }
429
430 /**
431 * Constructor.
432 *
433 * @param fingerprint fingerprint containing RSSI readings at an unknown location
434 * for provided located radio sources.
435 * @throws IllegalArgumentException if provided fingerprint is null.
436 */
437 protected SequentialRobustMixedPositionEstimator(
438 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint) {
439 internalSetFingerprint(fingerprint);
440 }
441
442 /**
443 * Constructor.
444 *
445 * @param sources located radio sources used for lateration.
446 * @param fingerprint fingerprint containing readings at an unknown location
447 * for provided located radio sources.
448 * @throws IllegalArgumentException if either provided sources or fingerprint is
449 * null or the number of provided sources is less
450 * than the required minimum.
451 */
452 protected SequentialRobustMixedPositionEstimator(
453 final List<? extends RadioSourceLocated<P>> sources,
454 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint) {
455 internalSetSources(sources);
456 internalSetFingerprint(fingerprint);
457 }
458
459 /**
460 * Constructor.
461 *
462 * @param listener listener in charge of handling events.
463 */
464 protected SequentialRobustMixedPositionEstimator(final SequentialRobustMixedPositionEstimatorListener<P> listener) {
465 this.listener = listener;
466 }
467
468 /**
469 * Constructor.
470 *
471 * @param sources located radio sources used for lateration.
472 * @param listener listener in charge of handling events.
473 * @throws IllegalArgumentException if provided sources is null or the number of
474 * provided sources is less than the required
475 * minimum.
476 */
477 protected SequentialRobustMixedPositionEstimator(
478 final List<? extends RadioSourceLocated<P>> sources,
479 final SequentialRobustMixedPositionEstimatorListener<P> listener) {
480 this(sources);
481 this.listener = listener;
482 }
483
484 /**
485 * Constructor.
486 *
487 * @param fingerprint fingerprint containing readings at an unknown location for
488 * provided located radio sources.
489 * @param listener listener in charge of handling events.
490 * @throws IllegalArgumentException if provided fingerprint is null.
491 */
492 protected SequentialRobustMixedPositionEstimator(
493 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint,
494 final SequentialRobustMixedPositionEstimatorListener<P> listener) {
495 this(fingerprint);
496 this.listener = listener;
497 }
498
499 /**
500 * Constructor.
501 *
502 * @param sources located radio sources used for lateration.
503 * @param fingerprint fingerprint containing readings at an unknown location
504 * for provided located radio sources.
505 * @param listener listener in charge of handling events.
506 * @throws IllegalArgumentException if either provided sources or fingerprint is
507 * null or the number of provided sources is less
508 * than the required minimum.
509 */
510 protected SequentialRobustMixedPositionEstimator(
511 final List<? extends RadioSourceLocated<P>> sources,
512 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint,
513 final SequentialRobustMixedPositionEstimatorListener<P> listener) {
514 this(sources, fingerprint);
515 this.listener = listener;
516 }
517
518 /**
519 * Constructor.
520 *
521 * @param sourceQualityScores quality scores corresponding to each
522 * provided located radio source. The
523 * larger the score value the better the
524 * quality of the radio source.
525 * @param fingerprintReadingQualityScores quality scores corresponding to
526 * readings within provided fingerprint.
527 * The larger the score the better the
528 * quality of the reading.
529 */
530 protected SequentialRobustMixedPositionEstimator(
531 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores) {
532 internalSetSourceQualityScores(sourceQualityScores);
533 internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
534 }
535
536 /**
537 * Constructor.
538 *
539 * @param sourceQualityScores quality scores corresponding to each
540 * provided located radio source. The
541 * larger the score value the better the
542 * quality of the radio source.
543 * @param fingerprintReadingQualityScores quality scores corresponding to readings
544 * within provided fingerprint. The larger
545 * the score the better the quality of the
546 * reading.
547 * @param sources located radio sources used for
548 * lateration.
549 * @throws IllegalArgumentException if provided sources is null or the number of
550 * provided sources is less than the required minimum.
551 */
552 protected SequentialRobustMixedPositionEstimator(
553 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
554 final List<? extends RadioSourceLocated<P>> sources) {
555 this(sources);
556 internalSetSourceQualityScores(sourceQualityScores);
557 internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
558 }
559
560 /**
561 * Constructor.
562 *
563 * @param sourceQualityScores quality scores corresponding to each
564 * provided located radio source. The
565 * larger the score value the better the
566 * quality of the radio source.
567 * @param fingerprintReadingQualityScores quality scores corresponding to readings
568 * within provided fingerprint. The larger
569 * the score the better the quality of the
570 * reading.
571 * @param fingerprint fingerprint containing readings at an
572 * unknown location for provided located
573 * radio sources.
574 * @throws IllegalArgumentException if provided fingerprint is null.
575 */
576 protected SequentialRobustMixedPositionEstimator(
577 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
578 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint) {
579 this(fingerprint);
580 internalSetSourceQualityScores(sourceQualityScores);
581 internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
582 }
583
584 /**
585 * Constructor.
586 *
587 * @param sourceQualityScores quality scores corresponding to each
588 * provided located radio source. The
589 * larger the score value the better the
590 * quality of the radio source.
591 * @param fingerprintReadingQualityScores quality scores corresponding to readings
592 * within provided fingerprint. The larger
593 * the score the better the quality of the
594 * reading.
595 * @param sources located radio sources used for
596 * lateration.
597 * @param fingerprint fingerprint containing readings at an
598 * unknown location for provided located
599 * radio sources.
600 * @throws IllegalArgumentException if either provided sources or fingerprint is null
601 * or the number of provided sources is less than the required minimum.
602 */
603 protected SequentialRobustMixedPositionEstimator(
604 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
605 final List<? extends RadioSourceLocated<P>> sources,
606 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint) {
607 this(sources, fingerprint);
608 internalSetSourceQualityScores(sourceQualityScores);
609 internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
610 }
611
612 /**
613 * Constructor.
614 *
615 * @param sourceQualityScores quality scores corresponding to each
616 * provided located radio source. The
617 * larger the score value the better the
618 * quality of the radio source.
619 * @param fingerprintReadingQualityScores quality scores corresponding to readings
620 * within provided fingerprint. The larger
621 * the score the better the quality of the
622 * reading.
623 * @param listener listener in charge of handling events.
624 */
625 protected SequentialRobustMixedPositionEstimator(
626 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
627 final SequentialRobustMixedPositionEstimatorListener<P> listener) {
628 this(sourceQualityScores, fingerprintReadingQualityScores);
629 this.listener = listener;
630 }
631
632 /**
633 * Constructor.
634 *
635 * @param sourceQualityScores quality scores corresponding to each
636 * provided located radio source. The
637 * larger the score value the better the
638 * quality of the radio source.
639 * @param fingerprintReadingQualityScores quality scores corresponding to readings
640 * within provided fingerprint. The larger
641 * the score the better the quality of the
642 * reading.
643 * @param sources located radio sources used for
644 * lateration.
645 * @param listener listener in charge of handling events.
646 * @throws IllegalArgumentException if provided sources is null or the number of
647 * provided sources is less than the required minimum.
648 */
649 protected SequentialRobustMixedPositionEstimator(
650 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
651 final List<? extends RadioSourceLocated<P>> sources,
652 final SequentialRobustMixedPositionEstimatorListener<P> listener) {
653 this(sourceQualityScores, fingerprintReadingQualityScores, sources);
654 this.listener = listener;
655 }
656
657 /**
658 * Constructor.
659 *
660 * @param sourceQualityScores quality scores corresponding to each
661 * provided located radio source. The
662 * larger the score value the better the
663 * quality of the radio source.
664 * @param fingerprintReadingQualityScores quality scores corresponding to
665 * readings within provided fingerprint.
666 * The larger the score the better the
667 * quality of the reading.
668 * @param fingerprint fingerprint containing readings at an
669 * unknown location for provided located
670 * radio sources.
671 * @param listener listener in charge of handling events.
672 * @throws IllegalArgumentException if provided fingerprint is null.
673 */
674 protected SequentialRobustMixedPositionEstimator(
675 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
676 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint,
677 final SequentialRobustMixedPositionEstimatorListener<P> listener) {
678 this(sourceQualityScores, fingerprintReadingQualityScores, fingerprint);
679 this.listener = listener;
680 }
681
682 /**
683 * Constructor.
684 *
685 * @param sourceQualityScores quality scores corresponding to each
686 * provided located radio source. The
687 * larger the score value the better the
688 * quality of the radio source.
689 * @param fingerprintReadingQualityScores quality scores corresponding to readings
690 * within provided fingerprint. The larger
691 * the score the better the quality of the
692 * reading.
693 * @param sources located radio sources used for
694 * lateration.
695 * @param fingerprint fingerprint containing readings at an
696 * unknown location for provided located radio
697 * sources.
698 * @param listener listener in charge of handling events.
699 * @throws IllegalArgumentException if either provided sources or fingerprint is null
700 * or the number of provided sources is less than the required minimum.
701 */
702 protected SequentialRobustMixedPositionEstimator(
703 final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
704 final List<? extends RadioSourceLocated<P>> sources,
705 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint,
706 final SequentialRobustMixedPositionEstimatorListener<P> listener) {
707 this(sourceQualityScores, fingerprintReadingQualityScores, sources, fingerprint);
708 this.listener = listener;
709 }
710
711 /**
712 * Gets robust method used for robust position estimation using ranging data.
713 *
714 * @return robust method used for robust position estimation using ranging data.
715 */
716 public RobustEstimatorMethod getRangingRobustMethod() {
717 return rangingRobustMethod;
718 }
719
720 /**
721 * Sets robust method for robust position estimation using ranging data.
722 *
723 * @param rangingRobustMethod robust method used for robust position estimation
724 * using ranging data.
725 * @throws LockedException if this instance is locked.
726 */
727 public void setRangingRobustMethod(final RobustEstimatorMethod rangingRobustMethod) throws LockedException {
728 if (isLocked()) {
729 throw new LockedException();
730 }
731
732 this.rangingRobustMethod = rangingRobustMethod;
733 }
734
735 /**
736 * Gets robust method used for coarse robust position estimation using RSSI data.
737 *
738 * @return robust method used for coarse robust position estimation using RSSI
739 * data.
740 */
741 public RobustEstimatorMethod getRssiRobustMethod() {
742 return rssiRobustMethod;
743 }
744
745 /**
746 * Sets robust method used for coarse robust position estimation using RSSI data.
747 *
748 * @param rssiRobustMethod robust method used for coarse robust position estimation
749 * using RSSI data.
750 * @throws LockedException if this instance is locked.
751 */
752 public void setRssiRobustMethod(final RobustEstimatorMethod rssiRobustMethod) throws LockedException {
753 if (isLocked()) {
754 throw new LockedException();
755 }
756
757 this.rssiRobustMethod = rssiRobustMethod;
758 }
759
760 /**
761 * Indicates whether located radio source position covariance is taken into account
762 * (if available) to determine distance standard deviation for ranging measurements.
763 *
764 * @return true to take into account radio source position covariance during ranging
765 * position estimation, false otherwise.
766 */
767 public boolean isRangingRadioSourcePositionCovarianceUsed() {
768 return useRangingRadioSourcePositionCovariance;
769 }
770
771 /**
772 * Specifies whether located radio source position covariance is taken into account
773 * (if available) to determine distance standard deviation for ranging measurements.
774 *
775 * @param useRangingRadioSourcePositionCovariance true to take into account radio
776 * source position covariance during
777 * ranging position estimation,
778 * false otherwise.
779 * @throws LockedException if this instance is locked.
780 */
781 public void setRangingRadioSourcePositionCovarianceUsed(
782 final boolean useRangingRadioSourcePositionCovariance) throws LockedException {
783 if (isLocked()) {
784 throw new LockedException();
785 }
786
787 this.useRangingRadioSourcePositionCovariance =
788 useRangingRadioSourcePositionCovariance;
789 }
790
791 /**
792 * Indicates whether located radio source position covariance is taken into account
793 * (if available) to determine distance standard deviation for RSSI measurements.
794 *
795 * @return true to take into account radio source position covariance during RSSI
796 * position estimation, false otherwise.
797 */
798 public boolean isRssiRadioSourcePositionCovarianceUsed() {
799 return useRssiRadioSourcePositionCovariance;
800 }
801
802 /**
803 * Specifies whether located radio source position covariance is taken into account
804 * (if available) to determine distance standard deviation for RSSI measurements.
805 *
806 * @param useRssiRadioSourcePositionCovariance true to take into account radio
807 * source position covariance during
808 * RSSI position estimation, false
809 * otherwise.
810 * @throws LockedException if this instance is locked.
811 */
812 public void setRssiRadioSourcePositionCovarianceUsed(final boolean useRssiRadioSourcePositionCovariance)
813 throws LockedException {
814 if (isLocked()) {
815 throw new LockedException();
816 }
817
818 this.useRssiRadioSourcePositionCovariance = useRssiRadioSourcePositionCovariance;
819 }
820
821 /**
822 * Indicates whether ranging readings are evenly distributed among radio sources
823 * taking into account quality scores of both radio sources and ranging readings.
824 *
825 * @return true if ranging readings are evenly distributed among radio sources,
826 * false otherwise.
827 */
828 public boolean isRangingReadingsEvenlyDistributed() {
829 return evenlyDistributeRangingReadings;
830 }
831
832 /**
833 * Specifies whether ranging readings are evenly distributed among radio sources
834 * taking into account quality scores of both radio sources and ranging readings.
835 *
836 * @param evenlyDistributeRangingReadings true if ranging readings are evenly
837 * distributed among radio sources, false
838 * otherwise.
839 * @throws LockedException if this instance is locked.
840 */
841 public void setRangingReadingsEvenlyDistributed(final boolean evenlyDistributeRangingReadings)
842 throws LockedException {
843 if (isLocked()) {
844 throw new LockedException();
845 }
846
847 this.evenlyDistributeRangingReadings = evenlyDistributeRangingReadings;
848 }
849
850 /**
851 * Gets distance standard deviation fallback value to use when none can be
852 * determined from provided RSSI measurements.
853 *
854 * @return distance standard deviation fallback value to use when none can be
855 * determined from provided RSSI measurements.
856 */
857 public double getRssiFallbackDistanceStandardDeviation() {
858 return rssiFallbackDistanceStandardDeviation;
859 }
860
861 /**
862 * Sets distance standard deviation fallback value to use when none can be
863 * determined from provided RSSI measurements.
864 *
865 * @param rssiFallbackDistanceStandardDeviation distance standard deviation fallback
866 * value to use when none can be
867 * determined from provided RSSI
868 * measurements.
869 * @throws LockedException if this instance is locked.
870 */
871 public void setRssiFallbackDistanceStandardDeviation(final double rssiFallbackDistanceStandardDeviation)
872 throws LockedException {
873 if (isLocked()) {
874 throw new LockedException();
875 }
876 this.rssiFallbackDistanceStandardDeviation = rssiFallbackDistanceStandardDeviation;
877 }
878
879 /**
880 * Gets distance standard deviation fallback value to use when none can be
881 * determined from provided ranging measurements.
882 *
883 * @return distance standard deviation fallback value to use when none can be
884 * determined from provided ranging measurements.
885 */
886 public double getRangingFallbackDistanceStandardDeviation() {
887 return rangingFallbackDistanceStandardDeviation;
888 }
889
890 /**
891 * Sets distance standard deviation fallback value to use when none can be
892 * determined from provided ranging measurements.
893 *
894 * @param rangingFallbackDistanceStandardDeviation distance standard deviation
895 * fallback value to use when none
896 * can be determined from provided
897 * ranging measurements.
898 * @throws LockedException if this instance is locked.
899 */
900 public void setRangingFallbackDistanceStandardDeviation(final double rangingFallbackDistanceStandardDeviation)
901 throws LockedException {
902 if (isLocked()) {
903 throw new LockedException();
904 }
905 this.rangingFallbackDistanceStandardDeviation = rangingFallbackDistanceStandardDeviation;
906 }
907
908 /**
909 * Indicates whether RSSI readings are evenly distributed among radio sources
910 * taking into account quality scores of both radio sources and RSSI readings.
911 *
912 * @return true if RSSI readings are evenly distributed among radio sources,
913 * false otherwise.
914 */
915 public boolean isRssiReadingsEvenlyDistributed() {
916 return evenlyDistributeRssiReadings;
917 }
918
919 /**
920 * Specifies whether RSSI readings are evenly distributed among radio sources
921 * taking into account quality scores of both radio sources and RSSI readings.
922 *
923 * @param evenlyDistributeRssiReadings true if RSSI readings are evenly distributed
924 * among radio sources, false otherwise.
925 * @throws LockedException if this instance is locked.
926 */
927 public void setRssiReadingsEvenlyDistributed(final boolean evenlyDistributeRssiReadings) throws LockedException {
928 if (isLocked()) {
929 throw new LockedException();
930 }
931
932 this.evenlyDistributeRssiReadings = evenlyDistributeRssiReadings;
933 }
934
935 /**
936 * Gets amount of progress variation before notifying a progress change during
937 * estimation.
938 *
939 * @return amount of progress variation before notifying a progress change during
940 * estimation.
941 */
942 public float getProgressDelta() {
943 return progressDelta;
944 }
945
946 /**
947 * Sets amount of progress variation before notifying a progress change during
948 * estimation.
949 *
950 * @param progressDelta amount of progress variation before notifying a progress
951 * change during estimation.
952 * @throws IllegalArgumentException if progress delta is less than zero or
953 * greater than 1.
954 * @throws LockedException if this instance is locked.
955 */
956 public void setProgressDelta(final float progressDelta) throws LockedException {
957 if (isLocked()) {
958 throw new LockedException();
959 }
960 if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
961 throw new IllegalArgumentException();
962 }
963 this.progressDelta = progressDelta;
964 }
965
966 /**
967 * Returns amount of confidence expressed as a value between 0.0 and 1.0 (which
968 * is equivalent to 100%) for robust position estimation on ranging data. The
969 * amount of confidence indicates the probability that the estimated result is
970 * correct. Usually this value will be close to 1.0, but not exactly 1.0.
971 *
972 * @return amount of confidence for robust position estimation as a value between
973 * 0.0 and 1.0.
974 */
975 public double getRangingConfidence() {
976 return rangingConfidence;
977 }
978
979 /**
980 * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which is
981 * equivalent to 100%) for robust position estimation on ranging data. The amount
982 * of confidence indicates the probability that the estimated result is correct.
983 * Usually this value will be close to 1.0, but not exactly 1.0.
984 *
985 * @param rangingConfidence confidence to be set for robust position estimation as
986 * a value between 0.0 and 1.0.
987 * @throws IllegalArgumentException if provided value is not between 0.0 and 1.0.
988 * @throws LockedException if estimator is locked.
989 */
990 public void setRangingConfidence(final double rangingConfidence) throws LockedException {
991 if (isLocked()) {
992 throw new LockedException();
993 }
994 if (rangingConfidence < MIN_CONFIDENCE || rangingConfidence > MAX_CONFIDENCE) {
995 throw new IllegalArgumentException();
996 }
997 this.rangingConfidence = rangingConfidence;
998 }
999
1000 /**
1001 * Returns amount of confidence expressed as a value between 0.0 and 1.0 (which is
1002 * equivalent to 100%) for robust position estimation on RSSI data. The amount of
1003 * confidence indicates the probability that the estimated result is correct.
1004 * Usually this value will be close to 1.0, but not exactly 1.0.
1005 *
1006 * @return amount of confidence for robust position estimation as a value between
1007 * 0.0 and 1.0.
1008 */
1009 public double getRssiConfidence() {
1010 return rssiConfidence;
1011 }
1012
1013 /**
1014 * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which is
1015 * equivalent to 100%) for robust position estimation on RSSI data. The amount
1016 * of confidence indicates the probability that the estimated result is correct.
1017 * Usually this value will be close to 1.0, but not exactly 1.0.
1018 *
1019 * @param rssiConfidence amount of confidence for robust position estimation as
1020 * a value between 0.0 and 1.0.
1021 * @throws IllegalArgumentException if provided value is not between 0.0 and 1.0.
1022 * @throws LockedException if estimator is locked.
1023 */
1024 public void setRssiConfidence(final double rssiConfidence) throws LockedException {
1025 if (isLocked()) {
1026 throw new LockedException();
1027 }
1028 if (rssiConfidence < MIN_CONFIDENCE || rssiConfidence > MAX_CONFIDENCE) {
1029 throw new IllegalArgumentException();
1030 }
1031 this.rssiConfidence = rssiConfidence;
1032 }
1033
1034 /**
1035 * Gets maximum allowed number of iterations for robust ranging position estimation.
1036 * When the maximum number of iterations is exceeded, an approximate result might
1037 * be available for retrieval.
1038 *
1039 * @return maximum allowed number of iterations for position estimation.
1040 */
1041 public int getRangingMaxIterations() {
1042 return rangingMaxIterations;
1043 }
1044
1045 /**
1046 * Sets maximum allowed number of iterations for robust ranging position
1047 * estimation.
1048 * When the maximum number of iterations is exceeded, an approximate result might
1049 * be available for retrieval.
1050 *
1051 * @param rangingMaxIterations maximum allowed number of iterations to be set for
1052 * position estimation.
1053 * @throws IllegalArgumentException if provided value is less than 1.
1054 * @throws LockedException if estimator is locked.
1055 */
1056 public void setRangingMaxIterations(final int rangingMaxIterations) throws LockedException {
1057 if (isLocked()) {
1058 throw new LockedException();
1059 }
1060 if (rangingMaxIterations < MIN_ITERATIONS) {
1061 throw new IllegalArgumentException();
1062 }
1063 this.rangingMaxIterations = rangingMaxIterations;
1064 }
1065
1066 /**
1067 * Gets maximum allowed number of iterations for robust RSSI position estimation.
1068 * When the maximum number of iterations is exceeded, an approximate result might
1069 * be available for retrieval.
1070 *
1071 * @return maximum allowed number of iterations for position estimation.
1072 */
1073 public int getRssiMaxIterations() {
1074 return rssiMaxIterations;
1075 }
1076
1077 /**
1078 * Sets maximum allowed number of iterations for robust RSSI position estimation.
1079 * When the maximum number of iterations is exceeded, an approximate result might
1080 * be available for retrieval.
1081 *
1082 * @param rssiMaxIterations maximum allowed number of iterations to be set for
1083 * position estimation.
1084 * @throws IllegalArgumentException if provided value is less than 1.
1085 * @throws LockedException if estimator is locked.
1086 */
1087 public void setRssiMaxIterations(final int rssiMaxIterations) throws LockedException {
1088 if (isLocked()) {
1089 throw new LockedException();
1090 }
1091 if (rssiMaxIterations < MIN_ITERATIONS) {
1092 throw new IllegalArgumentException();
1093 }
1094 this.rssiMaxIterations = rssiMaxIterations;
1095 }
1096
1097 /**
1098 * Indicates whether result is refined using all found inliers.
1099 *
1100 * @return true if result is refined, false otherwise.
1101 */
1102 public boolean isResultRefined() {
1103 return refineResult;
1104 }
1105
1106 /**
1107 * Specifies whether result is refined using all found inliers.
1108 *
1109 * @param refineResult true if result is refined, false otherwise.
1110 * @throws LockedException if this instance is locked.
1111 */
1112 public void setResultRefined(final boolean refineResult) throws LockedException {
1113 if (isLocked()) {
1114 throw new LockedException();
1115 }
1116
1117 this.refineResult = refineResult;
1118 }
1119
1120 /**
1121 * Indicates whether covariance must be kept after refining result.
1122 * This setting is only taken into account if result is refined.
1123 *
1124 * @return true if covariance must be kept after refining result, false otherwise.
1125 */
1126 public boolean isCovarianceKept() {
1127 return keepCovariance;
1128 }
1129
1130 /**
1131 * Specifies whether covariance must be kept after refining result.
1132 * This setting is only taken into account if result is refined.
1133 *
1134 * @param keepCovariance true if covariance must be kept after refining result,
1135 * false otherwise.
1136 * @throws LockedException if estimator is locked.
1137 */
1138 public void setCovarianceKept(final boolean keepCovariance) throws LockedException {
1139 if (isLocked()) {
1140 throw new LockedException();
1141 }
1142 this.keepCovariance = keepCovariance;
1143 }
1144
1145 /**
1146 * Indicates whether a linear solver is used for preliminary solution estimation
1147 * using ranging measurements.
1148 * The result obtained on each preliminary solution might be later refined.
1149 *
1150 * @return true if a linear solver is used for preliminary solution estimation on
1151 * ranging readings.
1152 */
1153 public boolean isRangingLinearSolverUsed() {
1154 return useRangingLinearSolver;
1155 }
1156
1157 /**
1158 * Specifies whether a linear solver is used for preliminary solution estimation
1159 * using ranging measurements.
1160 * The result obtained on each preliminary solution might be later refined.
1161 *
1162 * @param useRangingLinearSolver true if a linear solver is used for preliminary
1163 * solution estimation on ranging readings.
1164 * @throws LockedException if estimator is locked.
1165 */
1166 public void setRangingLinearSolverUsed(final boolean useRangingLinearSolver) throws LockedException {
1167 if (isLocked()) {
1168 throw new LockedException();
1169 }
1170 this.useRangingLinearSolver = useRangingLinearSolver;
1171 }
1172
1173 /**
1174 * Indicates whether a linear solver is used for preliminary solution estimation
1175 * using RSSI measurements.
1176 * The result obtained on each preliminary solution might be later refined.
1177 *
1178 * @return true if a linear solver is used for preliminary solution estimation on
1179 * RSSI readings.
1180 */
1181 public boolean isRssiLinearSolverUsed() {
1182 return useRssiLinearSolver;
1183 }
1184
1185 /**
1186 * Specifies whether a linear solver is used for preliminary solution estimation
1187 * using RSSI measurements.
1188 * The result obtained on each preliminary solution might be later refined.
1189 *
1190 * @param useRssiLinearSolver true if a linear solver is used for preliminary
1191 * solution estimation on RSSI readings.
1192 * @throws LockedException if estimator is locked.
1193 */
1194 public void setRssiLinearSolverUsed(final boolean useRssiLinearSolver) throws LockedException {
1195 if (isLocked()) {
1196 throw new LockedException();
1197 }
1198 this.useRssiLinearSolver = useRssiLinearSolver;
1199 }
1200
1201 /**
1202 * Indicates whether an homogeneous linear solver is used either to estimate
1203 * preliminary solutions or an initial solution for preliminary solutions that
1204 * will be later refined on the ranging fine estimation.
1205 *
1206 * @return true to use an homogeneous linear solver for preliminary solutions
1207 * during ranging fine position estimation.
1208 */
1209 public boolean isRangingHomogeneousLinearSolverUsed() {
1210 return useRangingHomogeneousLinearSolver;
1211 }
1212
1213 /**
1214 * Specifies whether an homogeneous linear solver is used either to estimate
1215 * preliminary solutions or an initial solution for preliminary solutions that
1216 * will be later refined on the ranging fine estimation.
1217 *
1218 * @param useRangingHomogeneousLinearSolver true to use an homogeneous linear
1219 * solver for preliminary solutions during
1220 * ranging fine position estimation.
1221 * @throws LockedException if estimator is locked.
1222 */
1223 public void setRangingHomogeneousLinearSolverUsed(final boolean useRangingHomogeneousLinearSolver)
1224 throws LockedException {
1225 if (isLocked()) {
1226 throw new LockedException();
1227 }
1228 this.useRangingHomogeneousLinearSolver = useRangingHomogeneousLinearSolver;
1229 }
1230
1231 /**
1232 * Indicates whether an homogeneous linear solver is used either to estimate
1233 * preliminary solutions or an initial solution for preliminary solutions that
1234 * will be later refined on the RSSI coarse estimation.
1235 *
1236 * @return true to use an homogeneous linear solver for preliminary solutions
1237 * during RSSI coarse position estimation.
1238 */
1239 public boolean isRssiHomogeneousLinearSolverUsed() {
1240 return useRssiHomogeneousLinearSolver;
1241 }
1242
1243 /**
1244 * Specifies whether an homogeneous linear solver is used either to estimate
1245 * preliminary solutions or an initial solution for preliminary solutions that
1246 * will be later refined on the RSSI coarse estimation.
1247 *
1248 * @param useRssiHomogeneousLinearSolver true to use an homogeneous linear
1249 * solver for preliminary solutions during
1250 * RSSI fine position estimation.
1251 * @throws LockedException if estimator is locked.
1252 */
1253 public void setRssiHomogeneousLinearSolverUsed(final boolean useRssiHomogeneousLinearSolver)
1254 throws LockedException {
1255 if (isLocked()) {
1256 throw new LockedException();
1257 }
1258 this.useRssiHomogeneousLinearSolver = useRssiHomogeneousLinearSolver;
1259 }
1260
1261 /**
1262 * Indicates whether preliminary ranging solutions are refined after an initial
1263 * linear solution is found.
1264 * If no initial preliminary solution is found using a linear solver, a non-linear
1265 * solver will be used regardless of this value using an average solution
1266 * as the initial value to be refined.
1267 *
1268 * @return true if preliminary ranging solutions must be refined after an initial
1269 * linear solution, false otherwise.
1270 */
1271 public boolean isRangingPreliminarySolutionRefined() {
1272 return refineRangingPreliminarySolutions;
1273 }
1274
1275 /**
1276 * Specifies whether preliminary ranging solutions are refined after an initial
1277 * linear solution is found.
1278 * If no initial preliminary solution is found using a linear solver, a non-linear
1279 * solver will be used regardless of this value using an average solution
1280 * as the initial value to be refined.
1281 *
1282 * @param refineRangingPreliminarySolutions true if preliminary ranging solutions
1283 * must be refined after an initial linear
1284 * solution, false otherwise.
1285 * @throws LockedException if estimator is locked.
1286 */
1287 public void setRangingPreliminarySolutionRefined(final boolean refineRangingPreliminarySolutions)
1288 throws LockedException {
1289 if (isLocked()) {
1290 throw new LockedException();
1291 }
1292 this.refineRangingPreliminarySolutions = refineRangingPreliminarySolutions;
1293 }
1294
1295 /**
1296 * Indicates whether preliminary RSSI solutions are refined after an initial
1297 * linear solution is found.
1298 * If no initial preliminary solution is found using a linear solver, a non-linear
1299 * solver will be used regardless of this value using an average solution
1300 * as the initial value to be refined.
1301 *
1302 * @return true if preliminary RSSI solutions must be refined after an initial
1303 * linear solution, false otherwise.
1304 */
1305 public boolean isRssiPreliminarySolutionRefined() {
1306 return refineRssiPreliminarySolutions;
1307 }
1308
1309 /**
1310 * Specifies whether preliminary RSSI solutions are refined after an initial
1311 * linear solution is found.
1312 * If no initial preliminary solution is found using a linear solver, a non-linear
1313 * solver will be used regardless of this value using an average solution
1314 * as the initial value ot be refined.
1315 *
1316 * @param refineRssiPreliminarySolutions true if preliminary RSSI solutions must
1317 * be refined after an initial linear
1318 * solution, false otherwise.
1319 * @throws LockedException if estimator is locked.
1320 */
1321 public void setRssiPreliminarySolutionRefined(final boolean refineRssiPreliminarySolutions) throws LockedException {
1322 if (isLocked()) {
1323 throw new LockedException();
1324 }
1325 this.refineRssiPreliminarySolutions = refineRssiPreliminarySolutions;
1326 }
1327
1328 /**
1329 * Gets size of subsets to be checked during ranging robust estimation.
1330 *
1331 * @return size of subsets to be checked during ranging robust estimation.
1332 */
1333 public int getRangingPreliminarySubsetSize() {
1334 return rangingPreliminarySubsetSize;
1335 }
1336
1337 /**
1338 * Sets size of subsets to be checked during ranging robust estimation.
1339 *
1340 * @param rangingPreliminarySubsetSize size of subsets to be checked during
1341 * ranging robust estimation.
1342 * @throws LockedException if estimator is locked.
1343 * @throws IllegalArgumentException if provided value is less than {@link #getMinRequiredSources()}.
1344 */
1345 public void setRangingPreliminarySubsetSize(final int rangingPreliminarySubsetSize) throws LockedException {
1346 if (isLocked()) {
1347 throw new LockedException();
1348 }
1349 if (rangingPreliminarySubsetSize < getMinRequiredSources()) {
1350 throw new IllegalArgumentException();
1351 }
1352
1353 this.rangingPreliminarySubsetSize = rangingPreliminarySubsetSize;
1354 }
1355
1356 /**
1357 * Gets size of subsets to be checked during RSSI robust estimation.
1358 *
1359 * @return size of subsets to be checked during RSSI robust estimation.
1360 */
1361 public int getRssiPreliminarySubsetSize() {
1362 return rssiPreliminarySubsetSize;
1363 }
1364
1365 /**
1366 * Sets size of subsets to be checked during RSSI robust estimation.
1367 *
1368 * @param rssiPreliminarySubsetSize size of subsets to be checked during
1369 * RSSI robust estimation.
1370 * @throws LockedException if estimator is locked.
1371 * @throws IllegalArgumentException if provided value is less than {@link #getMinRequiredSources()}.
1372 */
1373 public void setRssiPreliminarySubsetSize(final int rssiPreliminarySubsetSize) throws LockedException {
1374 if (isLocked()) {
1375 throw new LockedException();
1376 }
1377 if (rssiPreliminarySubsetSize < getMinRequiredSources()) {
1378 throw new IllegalArgumentException();
1379 }
1380
1381 this.rssiPreliminarySubsetSize = rssiPreliminarySubsetSize;
1382 }
1383
1384 /**
1385 * Gets threshold to determine when samples are inliers or not, used during robust
1386 * fine ranging position estimation.
1387 * If not defined, default threshold will be used.
1388 *
1389 * @return threshold for ranging estimation or null.
1390 */
1391 public Double getRangingThreshold() {
1392 return rangingThreshold;
1393 }
1394
1395 /**
1396 * Sets threshold to determine when samples are inliers or not, used during robust
1397 * fine ranging position estimation.
1398 * If not defined, default threshold will be used.
1399 *
1400 * @param rangingThreshold threshold for ranging estimation or null.
1401 * @throws LockedException if estimator is locked.
1402 */
1403 public void setRangingThreshold(final Double rangingThreshold) throws LockedException {
1404 if (isLocked()) {
1405 throw new LockedException();
1406 }
1407 this.rangingThreshold = rangingThreshold;
1408 }
1409
1410 /**
1411 * Gets threshold to determine when samples are inliers or not, used during robust
1412 * coarse RSSI position estimation.
1413 * If not defined, default threshold will be used.
1414 *
1415 * @return threshold for RSSI estimation or null.
1416 */
1417 public Double getRssiThreshold() {
1418 return rssiThreshold;
1419 }
1420
1421 /**
1422 * Sets threshold to determine when samples are inliers or not, used during robust
1423 * coarse RSSI position estimation.
1424 * If not defined, default threshold will be used.
1425 *
1426 * @param rssiThreshold threshold for RSSI estimation or null.
1427 * @throws LockedException if estimator is locked.
1428 */
1429 public void setRssiThreshold(final Double rssiThreshold) throws LockedException {
1430 if (isLocked()) {
1431 throw new LockedException();
1432 }
1433 this.rssiThreshold = rssiThreshold;
1434 }
1435
1436 /**
1437 * Gets located radio sources used for lateration.
1438 *
1439 * @return located radio sources used for lateration.
1440 */
1441 public List<RadioSourceLocated<P>> getSources() {
1442 //noinspection unchecked
1443 return (List<RadioSourceLocated<P>>) sources;
1444 }
1445
1446 /**
1447 * Sets located radio sources used for lateration.
1448 *
1449 * @param sources located radio sources used for lateration.
1450 * @throws LockedException if estimator is locked.
1451 * @throws IllegalArgumentException if provided value is null or the number of
1452 * provided sources is less than the required
1453 * minimum.
1454 */
1455 public void setSources(final List<? extends RadioSourceLocated<P>> sources) throws LockedException {
1456 if (isLocked()) {
1457 throw new LockedException();
1458 }
1459
1460 internalSetSources(sources);
1461 }
1462
1463 /**
1464 * Gets fingerprint containing readings at an unknown location for provided located
1465 * radio sources.
1466 *
1467 * @return fingerprint containing readings at an unknown location for provided
1468 * located radio sources.
1469 */
1470 public Fingerprint<RadioSource, Reading<RadioSource>> getFingerprint() {
1471 //noinspection unchecked
1472 return (Fingerprint<RadioSource, Reading<RadioSource>>) fingerprint;
1473 }
1474
1475 /**
1476 * Sets fingerprint containing readings at an unknown location for provided
1477 * located radio sources.
1478 *
1479 * @param fingerprint fingerprint containing readings at an unknown location for
1480 * provided located radio sources.
1481 * @throws LockedException if estimator is locked.
1482 */
1483 public void setFingerprint(
1484 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint)
1485 throws LockedException {
1486 if (isLocked()) {
1487 throw new LockedException();
1488 }
1489
1490 internalSetFingerprint(fingerprint);
1491 }
1492
1493 /**
1494 * Returns quality scores corresponding to each radio source.
1495 * The larger the score value the better the quality of the radio source.
1496 *
1497 * @return quality scores corresponding to each radio source.
1498 */
1499 public double[] getSourceQualityScores() {
1500 return sourceQualityScores;
1501 }
1502
1503 /**
1504 * Sets quality scores corresponding to each radio source.
1505 * The larger the score value the better the quality of the radio source.
1506 *
1507 * @param sourceQualityScores quality scores corresponding to each radio source.
1508 * @throws LockedException if this instance is locked.
1509 * @throws IllegalArgumentException if provided quality scores length is smaller
1510 * than minimum required samples.
1511 */
1512 public void setSourceQualityScores(final double[] sourceQualityScores) throws LockedException {
1513 if (isLocked()) {
1514 throw new LockedException();
1515 }
1516 internalSetSourceQualityScores(sourceQualityScores);
1517 }
1518
1519 /**
1520 * Gets quality scores corresponding to each reading within provided fingerprint.
1521 * The larger the score value the better the quality of the reading.
1522 *
1523 * @return quality scores corresponding to each reading within provided fingerprint.
1524 */
1525 public double[] getFingerprintReadingsQualityScores() {
1526 return fingerprintReadingsQualityScores;
1527 }
1528
1529 /**
1530 * Sets quality scores corresponding to each reading within provided fingerprint.
1531 * The larger the score value the better the quality of the reading.
1532 *
1533 * @param fingerprintReadingsQualityScores quality scores corresponding to each
1534 * reading within provided fingerprint.
1535 * @throws LockedException if this instance is locked.
1536 * @throws IllegalArgumentException if provided quality scores length is smaller
1537 * than minimum required samples.
1538 */
1539 public void setFingerprintReadingsQualityScores(final double[] fingerprintReadingsQualityScores)
1540 throws LockedException {
1541 if (isLocked()) {
1542 throw new LockedException();
1543 }
1544 internalSetFingerprintReadingsQualityScores(fingerprintReadingsQualityScores);
1545 }
1546
1547 /**
1548 * Gets listener to be notified of events raised by this instance.
1549 *
1550 * @return listener to be notified of events raised by this instance.
1551 */
1552 public SequentialRobustMixedPositionEstimatorListener<P> getListener() {
1553 return listener;
1554 }
1555
1556 /**
1557 * Sets listener to be notified of events raised by this instance.
1558 *
1559 * @param listener listener to be notified of events raised by this instance.
1560 * @throws LockedException if estimator is locked.
1561 */
1562 public void setListener(final SequentialRobustMixedPositionEstimatorListener<P> listener) throws LockedException {
1563 if (isLocked()) {
1564 throw new LockedException();
1565 }
1566 this.listener = listener;
1567 }
1568
1569 /**
1570 * Gets initial position to use as a starting point to find a new solution.
1571 * This is optional, but if provided, when no linear solvers are used, this is
1572 * taken into account. If linear solvers are used, this is ignored.
1573 *
1574 * @return an initial position.
1575 */
1576 public P getInitialPosition() {
1577 return initialPosition;
1578 }
1579
1580 /**
1581 * Sets initial position to use as a starting point to find a new solution.
1582 * This is optional, but if provided, when no linear solvers are used, this is
1583 * taken into account. If linear solvers are used, this is ignored.
1584 *
1585 * @param initialPosition an initial position.
1586 * @throws LockedException if estimator is locked.
1587 */
1588 public void setInitialPosition(final P initialPosition) throws LockedException {
1589 if (isLocked()) {
1590 throw new LockedException();
1591 }
1592 this.initialPosition = initialPosition;
1593 }
1594
1595 /**
1596 * Returns boolean indicating if estimator is locked because estimation is under
1597 * progress.
1598 *
1599 * @return true if estimator is locked, false otherwise.
1600 */
1601 public boolean isLocked() {
1602 return locked;
1603 }
1604
1605 /**
1606 * Indicates whether this instance is ready to start the estimation.
1607 *
1608 * @return true if this instance is ready, false otherwise.
1609 */
1610 public boolean isReady() {
1611 checkFingerprint(fingerprint);
1612
1613 final var numSources = sources != null ? sources.size() : 0;
1614 return numSources > getMinRequiredSources() && (rssiEstimatorAvailable || rangingEstimatorAvailable);
1615 }
1616
1617 /**
1618 * Estimates position based on provided located radio sources and readings of such
1619 * sources at an unknown location.
1620 *
1621 * @return estimated position.
1622 * @throws LockedException if estimator is locked.
1623 * @throws NotReadyException if estimator is not ready.
1624 * @throws RobustEstimatorException if estimation fails for some other reason.
1625 */
1626 public P estimate() throws LockedException, NotReadyException, RobustEstimatorException {
1627 if (isLocked()) {
1628 throw new LockedException();
1629 }
1630
1631 if (!isReady()) {
1632 throw new NotReadyException();
1633 }
1634
1635 if (rssiEstimatorAvailable) {
1636 buildRssiEstimator();
1637 setupRssiEstimator();
1638
1639 if (!rssiEstimator.isReady()) {
1640 throw new NotReadyException();
1641 }
1642 }
1643
1644 if (rangingEstimatorAvailable) {
1645 buildRangingEstimator();
1646 setupRangingEstimator();
1647
1648 if (!rangingEstimator.isReady()) {
1649 throw new NotReadyException();
1650 }
1651 }
1652
1653 locked = true;
1654 if (listener != null) {
1655 listener.onEstimateStart(this);
1656 }
1657
1658 var coarsePosition = initialPosition;
1659 if (rssiEstimator != null) {
1660 rssiEstimator.setInitialPosition(initialPosition);
1661
1662 try {
1663 // estimate coarse position using RSSI data
1664 coarsePosition = rssiEstimator.estimate();
1665 } catch (final RobustEstimatorException e) {
1666 coarsePosition = null;
1667 }
1668 }
1669
1670 // use coarse position as initial position for ranging estimation
1671 if (rangingEstimator != null) {
1672 rangingEstimator.setInitialPosition(coarsePosition != null ? coarsePosition : initialPosition);
1673 }
1674
1675 try {
1676 final var result = rangingEstimator != null ? rangingEstimator.estimate() : coarsePosition;
1677
1678 if (listener != null) {
1679 listener.onEstimateEnd(this);
1680 }
1681
1682 return result;
1683 } finally {
1684 locked = false;
1685 }
1686 }
1687
1688 /**
1689 * Gets data related to inliers found after estimation.
1690 *
1691 * @return data related to inliers found after estimation.
1692 */
1693 public InliersData getInliersData() {
1694 if (rangingEstimator != null) {
1695 return rangingEstimator.getInliersData();
1696 } else {
1697 return rssiEstimator != null ? rssiEstimator.getInliersData() : null;
1698 }
1699 }
1700
1701 /**
1702 * Gets known positions of radio sources used internally to solve lateration.
1703 *
1704 * @return known positions used internally.
1705 */
1706 public P[] getPositions() {
1707 if (rangingEstimator != null) {
1708 return rangingEstimator.getPositions();
1709 } else {
1710 return rssiEstimator != null ? rssiEstimator.getPositions() : null;
1711 }
1712 }
1713
1714 /**
1715 * Gets Euclidean distances from known located radio sources to the location of
1716 * provided readings in a fingerprint.
1717 * Distance values are used internally to solve lateration.
1718 *
1719 * @return Euclidean distances used internally.
1720 */
1721 public double[] getDistances() {
1722 if (rangingEstimator != null) {
1723 return rangingEstimator.getDistances();
1724 } else {
1725 return rssiEstimator != null ? rssiEstimator.getDistances() : null;
1726 }
1727 }
1728
1729 /**
1730 * Gets standard deviation distances from known located radio sources to the
1731 * location of provided readings in a fingerprint.
1732 * Distance standard deviations are used internally to solve lateration.
1733 *
1734 * @return standard deviations used internally.
1735 */
1736 public double[] getDistanceStandardDeviations() {
1737 if (rangingEstimator != null) {
1738 return rangingEstimator.getDistanceStandardDeviations();
1739 } else {
1740 return rssiEstimator != null ? rssiEstimator.getDistanceStandardDeviations() : null;
1741 }
1742 }
1743
1744 /**
1745 * Gets estimated covariance of estimated position if available.
1746 * This is only available when result has been refined and covariance is kept.
1747 *
1748 * @return estimated covariance or null.
1749 */
1750 public Matrix getCovariance() {
1751 if (rangingEstimator != null) {
1752 return rangingEstimator.getCovariance();
1753 } else {
1754 return rssiEstimator != null ? rssiEstimator.getCovariance() : null;
1755 }
1756 }
1757
1758 /**
1759 * Gets estimated position.
1760 *
1761 * @return estimated position.
1762 */
1763 public P getEstimatedPosition() {
1764 if (rangingEstimator != null) {
1765 return rangingEstimator.getEstimatedPosition();
1766 } else {
1767 return rssiEstimator != null ? rssiEstimator.getEstimatedPosition() : null;
1768 }
1769 }
1770
1771 /**
1772 * Gets number of dimensions of provided points.
1773 *
1774 * @return number of dimensions of provided points.
1775 */
1776 public abstract int getNumberOfDimensions();
1777
1778 /**
1779 * Gets minimum required number of located radio sources to perform lateration.
1780 *
1781 * @return minimum required number of located radio sources to perform
1782 * lateration.
1783 */
1784 public abstract int getMinRequiredSources();
1785
1786 /**
1787 * Builds ranging internal estimator.
1788 */
1789 protected abstract void buildRangingEstimator();
1790
1791 /**
1792 * Builds RSSI internal estimator.
1793 */
1794 protected abstract void buildRssiEstimator();
1795
1796 /**
1797 * Setup ranging internal estimator.
1798 *
1799 * @throws LockedException if estimator is locked.
1800 */
1801 protected void setupRangingEstimator() throws LockedException {
1802 if (fingerprint != null) {
1803 //builds separated ranging readings
1804 final var readings = fingerprint.getReadings();
1805
1806 final var rangingReadings = new ArrayList<RangingReading<RadioSource>>();
1807
1808 final var newFingerprintReadingsQualityScores = new double[numRangingReadings];
1809 var i = 0;
1810 var j = 0;
1811 for (final var reading : readings) {
1812 if (reading instanceof RangingReading) {
1813 //noinspection unchecked
1814 rangingReadings.add(
1815 (RangingReading<RadioSource>) reading);
1816 newFingerprintReadingsQualityScores[i] = this.fingerprintReadingsQualityScores[j];
1817 i++;
1818 } else if (reading instanceof RangingAndRssiReading) {
1819 //noinspection unchecked
1820 rangingReadings.add(createRangingReading((RangingAndRssiReading<RadioSource>) reading));
1821 newFingerprintReadingsQualityScores[i] = this.fingerprintReadingsQualityScores[j];
1822 i++;
1823 }
1824 j++;
1825 }
1826
1827 final var rangingFingerprint = new RangingFingerprint<>(rangingReadings);
1828
1829 // set data and configuration on both internal estimators
1830 rangingEstimator.setSources(sources);
1831 rangingEstimator.setFingerprint(rangingFingerprint);
1832 rangingEstimator.setRadioSourcePositionCovarianceUsed(useRangingRadioSourcePositionCovariance);
1833 rangingEstimator.setEvenlyDistributeReadings(evenlyDistributeRangingReadings);
1834 rangingEstimator.setFallbackDistanceStandardDeviation(rangingFallbackDistanceStandardDeviation);
1835 rangingEstimator.setProgressDelta(2.0f * progressDelta);
1836 rangingEstimator.setConfidence(rangingConfidence);
1837 rangingEstimator.setMaxIterations(rangingMaxIterations);
1838 rangingEstimator.setCovarianceKept(keepCovariance);
1839 rangingEstimator.setInitialPosition(initialPosition);
1840 rangingEstimator.setLinearSolverUsed(useRangingLinearSolver);
1841 rangingEstimator.setHomogeneousLinearSolverUsed(useRangingHomogeneousLinearSolver);
1842 rangingEstimator.setPreliminarySolutionRefined(refineRangingPreliminarySolutions);
1843 rangingEstimator.setSourceQualityScores(sourceQualityScores);
1844 rangingEstimator.setFingerprintReadingsQualityScores(newFingerprintReadingsQualityScores);
1845 rangingEstimator.setListener(new RobustRangingPositionEstimatorListener<>() {
1846 @Override
1847 public void onEstimateStart(final RobustRangingPositionEstimator<P> estimator) {
1848 // not used
1849 }
1850
1851 @Override
1852 public void onEstimateEnd(final RobustRangingPositionEstimator<P> estimator) {
1853 // not used
1854 }
1855
1856 @Override
1857 public void onEstimateNextIteration(
1858 final RobustRangingPositionEstimator<P> estimator, final int iteration) {
1859 // not used
1860 }
1861
1862 @Override
1863 public void onEstimateProgressChange(
1864 final RobustRangingPositionEstimator<P> estimator, final float progress) {
1865 if (listener != null) {
1866 final var p = rssiEstimatorAvailable ? 0.5f + 0.5f * progress : progress;
1867 listener.onEstimateProgressChange(SequentialRobustMixedPositionEstimator.this, p);
1868 }
1869 }
1870 });
1871
1872 rangingEstimator.setPreliminarySubsetSize(
1873 Math.max(rangingPreliminarySubsetSize, rangingEstimator.getMinRequiredSources()));
1874 }
1875 }
1876
1877 /**
1878 * Setup RSSI internal estimator.
1879 *
1880 * @throws LockedException if estimator is locked.
1881 */
1882 protected void setupRssiEstimator() throws LockedException {
1883 if (fingerprint != null) {
1884 // builds separated RSSI readings
1885 final var readings = fingerprint.getReadings();
1886
1887 final var rssiReadings = new ArrayList<RssiReading<RadioSource>>();
1888
1889 final var newFingerprintReadingsQualityScores = new double[numRssiReadings];
1890 var i = 0;
1891 var j = 0;
1892 for (final var reading : readings) {
1893 if (reading instanceof RssiReading) {
1894 //noinspection unchecked
1895 rssiReadings.add((RssiReading<RadioSource>) reading);
1896 newFingerprintReadingsQualityScores[i] = this.fingerprintReadingsQualityScores[j];
1897 i++;
1898 } else if (reading instanceof RangingAndRssiReading) {
1899 //noinspection unchecked
1900 rssiReadings.add(createRssiReading((RangingAndRssiReading<RadioSource>) reading));
1901 newFingerprintReadingsQualityScores[i] = this.fingerprintReadingsQualityScores[j];
1902 i++;
1903 }
1904 j++;
1905 }
1906
1907 final var rssiFingerprint = new RssiFingerprint<>(rssiReadings);
1908
1909 // set data and configuration on both internal estimators
1910 rssiEstimator.setSources(sources);
1911 rssiEstimator.setFingerprint(rssiFingerprint);
1912 rssiEstimator.setRadioSourcePositionCovarianceUsed(useRssiRadioSourcePositionCovariance);
1913 rssiEstimator.setEvenlyDistributeReadings(evenlyDistributeRssiReadings);
1914 rssiEstimator.setFallbackDistanceStandardDeviation(rssiFallbackDistanceStandardDeviation);
1915 rssiEstimator.setProgressDelta(2.0f * progressDelta);
1916 rssiEstimator.setConfidence(rssiConfidence);
1917 rssiEstimator.setMaxIterations(rssiMaxIterations);
1918 rssiEstimator.setResultRefined(refineResult);
1919 rssiEstimator.setCovarianceKept(keepCovariance);
1920 rssiEstimator.setInitialPosition(initialPosition);
1921 rssiEstimator.setLinearSolverUsed(useRssiLinearSolver);
1922 rssiEstimator.setHomogeneousLinearSolverUsed(useRssiHomogeneousLinearSolver);
1923 rssiEstimator.setPreliminarySolutionRefined(refineRssiPreliminarySolutions);
1924 rssiEstimator.setSourceQualityScores(sourceQualityScores);
1925 rssiEstimator.setFingerprintReadingsQualityScores(newFingerprintReadingsQualityScores);
1926 rssiEstimator.setListener(new RobustRssiPositionEstimatorListener<>() {
1927 @Override
1928 public void onEstimateStart(final RobustRssiPositionEstimator<P> estimator) {
1929 // not used
1930 }
1931
1932 @Override
1933 public void onEstimateEnd(final RobustRssiPositionEstimator<P> estimator) {
1934 // not used
1935 }
1936
1937 @Override
1938 public void onEstimateNextIteration(
1939 final RobustRssiPositionEstimator<P> estimator, final int iteration) {
1940 // not used
1941 }
1942
1943 @Override
1944 public void onEstimateProgressChange(
1945 final RobustRssiPositionEstimator<P> estimator, final float progress) {
1946 if (listener != null) {
1947 final var p = rangingEstimatorAvailable ? 0.5f * progress : progress;
1948 listener.onEstimateProgressChange(SequentialRobustMixedPositionEstimator.this, p);
1949 }
1950 }
1951 });
1952
1953 rssiEstimator.setPreliminarySubsetSize(
1954 Math.max(rssiPreliminarySubsetSize, rssiEstimator.getMinRequiredSources()));
1955 }
1956 }
1957
1958 /**
1959 * Internally sets located radio sources used for lateration.
1960 *
1961 * @param sources located radio sources used for lateration.
1962 * @throws IllegalArgumentException if provided value is null or the number of
1963 * provided sources is less than the required minimum.
1964 */
1965 private void internalSetSources(final List<? extends RadioSourceLocated<P>> sources) {
1966 if (sources == null) {
1967 throw new IllegalArgumentException();
1968 }
1969
1970 if (sources.size() < getMinRequiredSources()) {
1971 throw new IllegalArgumentException();
1972 }
1973
1974 this.sources = sources;
1975 }
1976
1977 /**
1978 * Internally sets fingerprint containing readings at an unknown location for
1979 * provided located radio sources.
1980 *
1981 * @param fingerprint fingerprint containing readings at an unknown location for
1982 * provided located radio sources.
1983 * @throws IllegalArgumentException if provided value is null.
1984 */
1985 private void internalSetFingerprint(
1986 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint) {
1987 if (fingerprint == null) {
1988 throw new IllegalArgumentException();
1989 }
1990
1991 this.fingerprint = fingerprint;
1992 }
1993
1994 /**
1995 * Sets quality scores corresponding to each provided located radio source.
1996 * This method is used internally and does not check whether instance is
1997 * locked or not.
1998 *
1999 * @param sourceQualityScores quality scores to be set.
2000 * @throws IllegalArgumentException if provided quality scores length is
2001 * smaller than 3 samples for 2D or 4 samples for 3D.
2002 */
2003 private void internalSetSourceQualityScores(final double[] sourceQualityScores) {
2004 if (sourceQualityScores == null ||
2005 sourceQualityScores.length < getMinRequiredSources()) {
2006 throw new IllegalArgumentException();
2007 }
2008
2009 this.sourceQualityScores = sourceQualityScores;
2010 }
2011
2012 /**
2013 * Sets quality scores corresponding to each provided reading within provided
2014 * fingerprint.
2015 * This method is used internally and does not check whether instance is locked
2016 * or not.
2017 *
2018 * @param fingerprintReadingsQualityScores quality scores to be set.
2019 * @throws IllegalArgumentException if provided quality scores length is smaller
2020 * than 3 samples for 2D or 4 samples for 3D.
2021 */
2022 private void internalSetFingerprintReadingsQualityScores(final double[] fingerprintReadingsQualityScores) {
2023 if (fingerprintReadingsQualityScores == null
2024 || fingerprintReadingsQualityScores.length < getMinRequiredSources()) {
2025 throw new IllegalArgumentException();
2026 }
2027
2028 this.fingerprintReadingsQualityScores = fingerprintReadingsQualityScores;
2029 }
2030
2031 /**
2032 * Creates a ranging reading from a ranging and RSSI reading.
2033 *
2034 * @param reading input reading to convert from.
2035 * @return a ranging reading containing only the ranging data of input reading.
2036 */
2037 private RangingReading<RadioSource> createRangingReading(
2038 final RangingAndRssiReading<? extends RadioSource> reading) {
2039 return new RangingReading<>(reading.getSource(), reading.getDistance(), reading.getDistanceStandardDeviation(),
2040 reading.getNumAttemptedMeasurements(), reading.getNumSuccessfulMeasurements());
2041 }
2042
2043 /**
2044 * Creates an RSSI reading from a ranging and RSSI reading.
2045 *
2046 * @param reading input reading to convert from.
2047 * @return an RSSI reading containing only the RSSI data of input reading.
2048 */
2049 private RssiReading<RadioSource> createRssiReading(final RangingAndRssiReading<? extends RadioSource> reading) {
2050 return new RssiReading<>(reading.getSource(), reading.getRssi(), reading.getRssiStandardDeviation());
2051 }
2052
2053 /**
2054 * Checks readings within provided fingerprint to determine the amount of available
2055 * ranging or RSSI readings.
2056 *
2057 * @param fingerprint fingerprint to be checked.
2058 */
2059 private void checkFingerprint(
2060 final Fingerprint<? extends RadioSource, ? extends Reading<? extends RadioSource>> fingerprint) {
2061 checkReadings(fingerprint != null ? fingerprint.getReadings() : null);
2062 }
2063
2064 /**
2065 * Checks number of available ranging readings and number of available RSSI
2066 * readings. Also determines whether position must be estimated using ranging
2067 * data or RSSI data.
2068 *
2069 * @param readings readings to be checked.
2070 */
2071 private void checkReadings(final List<? extends Reading<?>> readings) {
2072 numRssiReadings = 0;
2073 numRangingReadings = 0;
2074 rangingEstimatorAvailable = rssiEstimatorAvailable = false;
2075
2076 if (readings == null) {
2077 return;
2078 }
2079
2080 for (final var reading : readings) {
2081 if (reading instanceof RangingReading) {
2082 numRangingReadings++;
2083 } else if (reading instanceof RssiReading) {
2084 numRssiReadings++;
2085 } else if (reading instanceof RangingAndRssiReading) {
2086 numRangingReadings++;
2087 numRssiReadings++;
2088 }
2089 }
2090
2091 final var min = getMinRequiredSources();
2092 rangingEstimatorAvailable = numRangingReadings >= min;
2093 rssiEstimatorAvailable = numRssiReadings >= min;
2094 }
2095 }