1 /*
2 * Copyright (C) 2018 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.radiosource;
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.RadioSourceLocated;
23 import com.irurueta.navigation.indoor.ReadingLocated;
24 import com.irurueta.numerical.robust.InliersData;
25 import com.irurueta.numerical.robust.RobustEstimatorException;
26 import com.irurueta.numerical.robust.RobustEstimatorMethod;
27
28 import java.util.List;
29
30 /**
31 * Robustly estimates a radio source. Usually this implies at least the estimation of
32 * the radio source location, however, implementations of this class might estimate
33 * additional parameters.
34 *
35 * @param <P> a {@link Point} type.
36 * @param <R> a {@link ReadingLocated} type.
37 * @param <L> a {@link RobustRadioSourceEstimatorListener} type.
38 */
39 public abstract class RobustRadioSourceEstimator<P extends Point<?>, R extends ReadingLocated<P>,
40 L extends RobustRadioSourceEstimatorListener<? extends RobustRadioSourceEstimator<?, ?, ?>>> {
41
42 /**
43 * Default robust estimator method when none is provided.
44 */
45 public static final RobustEstimatorMethod DEFAULT_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
46
47 /**
48 * Indicates that result is refined by default using all found inliers.
49 */
50 public static final boolean DEFAULT_REFINE_RESULT = true;
51
52 /**
53 * Indicates that covariance is kept by default after refining result.
54 */
55 public static final boolean DEFAULT_KEEP_COVARIANCE = true;
56
57 /**
58 * Default amount of progress variation before notifying a change in estimation progress.
59 * By default, this is set to 5%.
60 */
61 public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
62
63 /**
64 * Minimum allowed value for progress delta.
65 */
66 public static final float MIN_PROGRESS_DELTA = 0.0f;
67
68 /**
69 * Maximum allowed value for progress delta.
70 */
71 public static final float MAX_PROGRESS_DELTA = 1.0f;
72
73 /**
74 * Constant defining default confidence of the estimated result, which is
75 * 99%. This means that with a probability of 99% estimation will be
76 * accurate because chosen sub-samples will be inliers.
77 */
78 public static final double DEFAULT_CONFIDENCE = 0.99;
79
80 /**
81 * Default maximum allowed number of iterations.
82 */
83 public static final int DEFAULT_MAX_ITERATIONS = 5000;
84
85 /**
86 * Minimum allowed confidence value.
87 */
88 public static final double MIN_CONFIDENCE = 0.0;
89
90 /**
91 * Maximum allowed confidence value.
92 */
93 public static final double MAX_CONFIDENCE = 1.0;
94
95 /**
96 * Minimum allowed number of iterations.
97 */
98 public static final int MIN_ITERATIONS = 1;
99
100 /**
101 * Signal readings belonging to the same radio source to be estimated.
102 */
103 protected List<? extends R> readings;
104
105 /**
106 * Listener to be notified of events such as when estimation starts, ends or its
107 * progress significantly changes.
108 */
109 protected L listener;
110
111 /**
112 * Estimated position.
113 */
114 protected P estimatedPosition;
115
116 /**
117 * Indicates if this instance is locked because estimation is being executed.
118 */
119 protected boolean locked;
120
121 /**
122 * Amount of progress variation before notifying a progress change during estimation.
123 */
124 protected float progressDelta = DEFAULT_PROGRESS_DELTA;
125
126 /**
127 * Amount of confidence expressed as a value between 0.0 and 1.0 (which is equivalent
128 * to 100%). The amount of confidence indicates the probability that the estimated
129 * result is correct. Usually this value will be close to 1.0, but not exactly 1.0.
130 */
131 protected double confidence = DEFAULT_CONFIDENCE;
132
133 /**
134 * Maximum allowed number of iterations. When the maximum number of iterations is
135 * exceeded, result will not be available, however an approximate result will be
136 * available for retrieval.
137 */
138 protected int maxIterations = DEFAULT_MAX_ITERATIONS;
139
140 /**
141 * Data related to inliers found after estimation.
142 */
143 protected InliersData inliersData;
144
145 /**
146 * Indicates whether result must be refined using found inliers.
147 * If true, inliers will be computed and kept in any implementation regardless of the
148 * settings.
149 */
150 protected boolean refineResult = DEFAULT_REFINE_RESULT;
151
152 /**
153 * Indicates whether covariance must be kept after refining result.
154 * This setting is only taken into account if result is refined.
155 */
156 protected boolean keepCovariance = DEFAULT_KEEP_COVARIANCE;
157
158 /**
159 * Covariance of estimated position, power and/or path-loss exponent.
160 * This is only available when result has been refined and covariance is kept.
161 */
162 protected Matrix covariance;
163
164 /**
165 * Covariance of estimated position.
166 * Size of this matrix will depend on the number of dimensions
167 * of estimated position (either 2 or 3).
168 * This value will only be available when position estimation is enabled.
169 */
170 protected Matrix estimatedPositionCovariance;
171
172 /**
173 * Size of subsets to be checked during robust estimation.
174 */
175 protected int preliminarySubsetSize;
176
177 /**
178 * Constructor.
179 */
180 protected RobustRadioSourceEstimator() {
181 }
182
183 /**
184 * Constructor.
185 * Sets located radio signal readings belonging to the same radio source.
186 *
187 * @param readings radio signal readings belonging to the same
188 * radio source.
189 * @throws IllegalArgumentException if readings are not valid.
190 */
191 protected RobustRadioSourceEstimator(final List<? extends R> readings) {
192 internalSetReadings(readings);
193 }
194
195 /**
196 * Constructor.
197 *
198 * @param listener listener in charge of attending events raised by this instance.
199 */
200 protected RobustRadioSourceEstimator(final L listener) {
201 this.listener = listener;
202 }
203
204 /**
205 * Constructor.
206 * Sets located radio signal readings belonging to the same radio source.
207 *
208 * @param readings radio signal readings belonging to the same
209 * radio source.
210 * @param listener listener in charge of attending events raised by this instance.
211 * @throws IllegalArgumentException if readings are not valid.
212 */
213 protected RobustRadioSourceEstimator(final List<? extends R> readings, final L listener) {
214 this(readings);
215 this.listener = listener;
216 }
217
218 /**
219 * Indicates whether estimator is locked during estimation.
220 *
221 * @return true if estimator is locked, false otherwise.
222 */
223 public boolean isLocked() {
224 return locked;
225 }
226
227 /**
228 * Returns amount of progress variation before notifying a progress change during
229 * estimation.
230 *
231 * @return amount of progress variation before notifying a progress change during
232 * estimation.
233 */
234 public float getProgressDelta() {
235 return progressDelta;
236 }
237
238 /**
239 * Sets amount of progress variation before notifying a progress change during
240 * estimation.
241 *
242 * @param progressDelta amount of progress variation before notifying a progress
243 * change during estimation.
244 * @throws IllegalArgumentException if progress delta is less than zero or greater than 1.
245 * @throws LockedException if this estimator is locked.
246 */
247 public void setProgressDelta(final float progressDelta) throws LockedException {
248 if (isLocked()) {
249 throw new LockedException();
250 }
251 if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
252 throw new IllegalArgumentException();
253 }
254 this.progressDelta = progressDelta;
255 }
256
257 /**
258 * Returns amount of confidence expressed as a value between 0.0 and 1.0
259 * (which is equivalent to 100%). The amount of confidence indicates the probability
260 * that the estimated result is correct. Usually this value will be close to 1.0, but
261 * not exactly 1.0.
262 *
263 * @return amount of confidence as a value between 0.0 and 1.0.
264 */
265 public double getConfidence() {
266 return confidence;
267 }
268
269 /**
270 * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which is
271 * equivalent to 100%). The amount of confidence indicates the probability that
272 * the estimated result is correct. Usually this value will be close to 1.0, but
273 * not exactly 1.0.
274 *
275 * @param confidence confidence to be set as a value between 0.0 and 1.0.
276 * @throws IllegalArgumentException if provided value is not between 0.0 and 1.0.
277 * @throws LockedException if estimator is locked.
278 */
279 public void setConfidence(final double confidence) throws LockedException {
280 if (isLocked()) {
281 throw new LockedException();
282 }
283 if (confidence < MIN_CONFIDENCE || confidence > MAX_CONFIDENCE) {
284 throw new IllegalArgumentException();
285 }
286 this.confidence = confidence;
287 }
288
289 /**
290 * Returns maximum allowed number of iterations. If maximum allowed number of
291 * iterations is achieved without converging to a result when calling estimate(),
292 * a RobustEstimatorException will be raised.
293 *
294 * @return maximum allowed number of iterations.
295 */
296 public int getMaxIterations() {
297 return maxIterations;
298 }
299
300 /**
301 * Sets maximum allowed number of iterations. When the maximum number of iterations
302 * is exceeded, result will not be available, however an approximate result will be
303 * available for retrieval.
304 *
305 * @param maxIterations maximum allowed number of iterations to be set.
306 * @throws IllegalArgumentException if provided value is less than 1.
307 * @throws LockedException if this estimator is locked.
308 */
309 public void setMaxIterations(final int maxIterations) throws LockedException {
310 if (isLocked()) {
311 throw new LockedException();
312 }
313 if (maxIterations < MIN_ITERATIONS) {
314 throw new IllegalArgumentException();
315 }
316 this.maxIterations = maxIterations;
317 }
318
319 /**
320 * Gets data related to inliers found after estimation.
321 *
322 * @return data related to inliers found after estimation.
323 */
324 public InliersData getInliersData() {
325 return inliersData;
326 }
327
328 /**
329 * Indicates whether result must be refined using a non-linear solver over found inliers.
330 *
331 * @return true to refine result, false to simply use result found by robust estimator
332 * without further refining.
333 */
334 public boolean isResultRefined() {
335 return refineResult;
336 }
337
338 /**
339 * Specifies whether result must be refined using a non-linear solver over found inliers.
340 *
341 * @param refineResult true to refine result, false to simply use result found by robust
342 * estimator without further refining.
343 * @throws LockedException if estimator is locked.
344 */
345 public void setResultRefined(final boolean refineResult) throws LockedException {
346 if (isLocked()) {
347 throw new LockedException();
348 }
349 this.refineResult = refineResult;
350 }
351
352 /**
353 * Indicates whether covariance must be kept after refining result.
354 * This setting is only taken into account if result is refined.
355 *
356 * @return true if covariance must be kept after refining result, false otherwise.
357 */
358 public boolean isCovarianceKept() {
359 return keepCovariance;
360 }
361
362 /**
363 * Specifies whether covariance must be kept after refining result.
364 * This setting is only taken into account if result is refined.
365 *
366 * @param keepCovariance true if covariance must be kept after refining result,
367 * false otherwise.
368 * @throws LockedException if estimator is locked.
369 */
370 public void setCovarianceKept(final boolean keepCovariance) throws LockedException {
371 if (isLocked()) {
372 throw new LockedException();
373 }
374 this.keepCovariance = keepCovariance;
375 }
376
377 /**
378 * Gets signal readings belonging to the same radio source.
379 *
380 * @return signal readings belonging to the same radio source.
381 */
382 public List<R> getReadings() {
383 //noinspection unchecked
384 return (List<R>) readings;
385 }
386
387 /**
388 * Sets signal readings belonging to the same radio source.
389 *
390 * @param readings signal readings belonging to the same
391 * radio source.
392 * @throws LockedException if estimator is locked.
393 * @throws IllegalArgumentException if readings are not valid.
394 */
395 public void setReadings(final List<? extends R> readings) throws LockedException {
396 if (isLocked()) {
397 throw new LockedException();
398 }
399
400 internalSetReadings(readings);
401 }
402
403 /**
404 * Gets listener in charge of attending events raised by this instance.
405 *
406 * @return listener in charge of attending events raised by this instance.
407 */
408 public L getListener() {
409 return listener;
410 }
411
412 /**
413 * Sets listener in charge of attending events raised by this instance.
414 *
415 * @param listener listener in charge of attending events raised by this
416 * instance.
417 * @throws LockedException if estimator is locked.
418 */
419 public void setListener(final L listener) throws LockedException {
420 if (isLocked()) {
421 throw new LockedException();
422 }
423
424 this.listener = listener;
425 }
426
427 /**
428 * Returns quality scores corresponding to each pair of
429 * positions and distances (i.e. sample).
430 * The larger the score value the better the quality of the sample.
431 * This implementation always returns null.
432 * Subclasses using quality scores must implement proper behavior.
433 *
434 * @return quality scores corresponding to each sample.
435 */
436 public double[] getQualityScores() {
437 return null;
438 }
439
440 /**
441 * Sets quality scores corresponding to each pair of positions and
442 * distances (i.e. sample).
443 * The larger the score value the better the quality of the sample.
444 * This implementation makes no action.
445 * Subclasses using quality scores must implement proper behaviour.
446 *
447 * @param qualityScores quality scores corresponding to each pair of
448 * matched points.
449 * @throws IllegalArgumentException if provided quality scores length
450 * is smaller than minimum required samples.
451 * @throws LockedException if robust solver is locked because an
452 * estimation is already in progress.
453 */
454 public void setQualityScores(final double[] qualityScores) throws LockedException {
455 }
456
457 /**
458 * Gets size of subsets to be checked during robust estimation.
459 * This has to be at least {@link #getMinReadings()}
460 *
461 * @return size of subsets to be checked during robust estimation.
462 */
463 public int getPreliminarySubsetSize() {
464 return preliminarySubsetSize;
465 }
466
467 /**
468 * Sets size of subsets to be checked during estimation.
469 * This has to be at least {@link #getMinReadings()}.
470 *
471 * @param preliminarySubsetSize size of subsets to be checked during robust estimation.
472 * @throws LockedException if instance is busy solving the lateration problem.
473 * @throws IllegalArgumentException if provided value is less than {@link #getMinReadings()}.
474 */
475 public void setPreliminarySubsetSize(final int preliminarySubsetSize) throws LockedException {
476 if (isLocked()) {
477 throw new LockedException();
478 }
479 if (preliminarySubsetSize < getMinReadings()) {
480 throw new IllegalArgumentException();
481 }
482
483 this.preliminarySubsetSize = preliminarySubsetSize;
484 }
485
486 /**
487 * Gets covariance for estimated position, power and path-loss.
488 * Matrix contains information in the following order:
489 * Top-left sub-matrix contains covariance of position,
490 * then follows transmitted power variance, and finally
491 * the last element contains path-loss exponent variance.
492 * This is only available when result has been refined and covariance is kept.
493 *
494 * @return covariance for estimated position and power.
495 */
496 public Matrix getCovariance() {
497 return covariance;
498 }
499
500 /**
501 * Gets estimated position covariance.
502 * Size of this matrix will depend on the number of dimensions
503 * of estimated position (either 2 or 3).
504 * This is only available when result has been refined and covariance is kept.
505 *
506 * @return estimated position covariance.
507 */
508 public Matrix getEstimatedPositionCovariance() {
509 return estimatedPositionCovariance;
510 }
511
512 /**
513 * Gets estimated position.
514 *
515 * @return estimated position.
516 */
517 public P getEstimatedPosition() {
518 return estimatedPosition;
519 }
520
521 /**
522 * Indicates whether readings are valid or not.
523 * Readings are considered valid when there are enough readings.
524 *
525 * @param readings readings to be validated.
526 * @return true if readings are valid, false otherwise.
527 */
528 public boolean areValidReadings(final List<? extends R> readings) {
529 return readings != null && readings.size() >= getMinReadings();
530 }
531
532 /**
533 * Indicates whether this instance is ready to start the estimation.
534 *
535 * @return true if this instance is ready, false otherwise.
536 */
537 public abstract boolean isReady();
538
539 /**
540 * Gets minimum required number of readings to estimate
541 * power, position and path-loss exponent.
542 * This value depends on the number of parameters to
543 * be estimated, but for position only, this is 3
544 * readings for 2D, and 4 readings for 3D.
545 *
546 * @return minimum required number of readings.
547 */
548 public abstract int getMinReadings();
549
550 /**
551 * Gets number of dimensions of position points.
552 *
553 * @return number of dimensions of position points.
554 */
555 public abstract int getNumberOfDimensions();
556
557 /**
558 * Robustly estimates position, transmitted power and path-loss exponent for a
559 * radio source.
560 *
561 * @throws LockedException if instance is busy during estimation.
562 * @throws NotReadyException if estimator is not ready.
563 * @throws RobustEstimatorException if estimation fails for any reason
564 * (i.e. numerical instability, no solution available, etc).
565 */
566 public abstract void estimate() throws LockedException, NotReadyException, RobustEstimatorException;
567
568 /**
569 * Gets estimated located radio source.
570 *
571 * @param <S> type of located radio source.
572 * @return estimated located radio source.
573 */
574 public abstract <S extends RadioSourceLocated<P>> S getEstimatedRadioSource();
575
576 /**
577 * Internally sets signal readings belonging to the same radio source.
578 *
579 * @param readings signal readings belonging to the same radio source.
580 * @throws IllegalArgumentException if readings are null, not enough readings
581 * are available, or readings do not belong to the same access point.
582 */
583 protected void internalSetReadings(final List<? extends R> readings) {
584 if (!areValidReadings(readings)) {
585 throw new IllegalArgumentException();
586 }
587
588 this.readings = readings;
589 }
590 }