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.geometry.Point2D;
19 import com.irurueta.navigation.LockedException;
20 import com.irurueta.navigation.NotReadyException;
21 import com.irurueta.navigation.indoor.RadioSource;
22 import com.irurueta.navigation.indoor.RssiReadingLocated;
23 import com.irurueta.numerical.robust.PROMedSRobustEstimator;
24 import com.irurueta.numerical.robust.PROMedSRobustEstimatorListener;
25 import com.irurueta.numerical.robust.RobustEstimator;
26 import com.irurueta.numerical.robust.RobustEstimatorException;
27 import com.irurueta.numerical.robust.RobustEstimatorMethod;
28
29 import java.util.List;
30
31 /**
32 * Robustly estimate 2D position, transmitted power and path-loss exponent of a radio source
33 * (e.g. Wi-Fi access point or bluetooth beacon), by discarding outliers using PROMedS
34 * algorithm and assuming that the radio source emits isotropically following the
35 * expression below:
36 * Pr = Pt*Gt*Gr*lambda^2 / (4*pi*d)^2,
37 * where Pr is the received power (expressed in mW),
38 * Gt is the Gain of the transmission antenna
39 * Gr is the Gain of the receiver antenna
40 * d is the distance between emitter and receiver
41 * and lambda is the wavelength and is equal to: lambda = c / f,
42 * where c is the speed of light
43 * and f is the carrier frequency of the radio signal.
44 * Because usually information about the antenna of the radio source cannot be
45 * retrieved (because many measurements are made on unknown devices where
46 * physical access is not possible), this implementation will estimate the
47 * equivalent transmitted power as: Pte = Pt * Gt * Gr.
48 * If RssiReadings contain RSSI standard deviations, those values will be used,
49 * otherwise it will be assumed an RSSI standard deviation of 1 dB.
50 * Implementations of this class should be able to detect and discard outliers in
51 * order to find the best solution.
52 * <p>
53 * IMPORTANT: When using this class estimation can be done using a
54 * combination of radio source position, transmitted power and path loss
55 * exponent. However enabling all three estimations usually achieves
56 * inaccurate results. When using this class, estimation must be of at least
57 * one parameter (position, transmitted power or path loss exponent) when
58 * initial values are provided for the other two, and at most it should consist
59 * of two parameters (either position and transmitted power, position and
60 * path loss exponent or transmitted power and path loss exponent), providing an
61 * initial value for the remaining parameter.
62 *
63 * @param <S> a {@link RadioSource} type.
64 */
65 @SuppressWarnings("Duplicates")
66 public class PROMedSRobustRssiRadioSourceEstimator2D<S extends RadioSource> extends
67 RobustRssiRadioSourceEstimator2D<S> {
68
69 /**
70 * Default value to be used for stop threshold. Stop threshold can be used to
71 * avoid keeping the algorithm unnecessarily iterating in case that best
72 * estimated threshold using median of residuals is not small enough. Once a
73 * solution is found that generates a threshold below this value, the
74 * algorithm will stop.
75 * The stop threshold can be used to prevent the LMedS algorithm iterating
76 * too many times in cases where samples have a very similar accuracy.
77 * For instance, in cases where proportion of outliers is very small (close
78 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
79 * iterate for a long time trying to find the best solution when indeed
80 * there is no need to do that if a reasonable threshold has already been
81 * reached.
82 * Because of this behaviour the stop threshold can be set to a value much
83 * lower than the one typically used in RANSAC, and yet the algorithm could
84 * still produce even smaller thresholds in estimated results.
85 */
86 public static final double DEFAULT_STOP_THRESHOLD = 1e-4;
87
88 /**
89 * Minimum allowed stop threshold value.
90 */
91 public static final double MIN_STOP_THRESHOLD = 0.0;
92
93 /**
94 * Threshold to be used to keep the algorithm iterating in case that best
95 * estimated threshold using median of residuals is not small enough. Once
96 * a solution is found that generates a threshold below this value, the
97 * algorithm will stop.
98 * The stop threshold can be used to prevent the LMedS algorithm iterating
99 * too many times in cases where samples have a very similar accuracy.
100 * For instance, in cases where proportion of outliers is very small (close
101 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
102 * iterate for a long time trying to find the best solution when indeed
103 * there is no need to do that if a reasonable threshold has already been
104 * reached.
105 * Because of this behaviour the stop threshold can be set to a value much
106 * lower than the one typically used in RANSAC, and yet the algorithm could
107 * still produce even smaller thresholds in estimated results.
108 */
109 private double stopThreshold = DEFAULT_STOP_THRESHOLD;
110
111 /**
112 * Quality scores corresponding to each provided sample.
113 * The larger the score value the better the quality of the sample.
114 */
115 private double[] qualityScores;
116
117 /**
118 * Constructor.
119 */
120 public PROMedSRobustRssiRadioSourceEstimator2D() {
121 super();
122 }
123
124 /**
125 * Constructor.
126 * Sets signal readings belonging to the same radio source.
127 *
128 * @param readings signal readings belonging to the same radio source.
129 * @throws IllegalArgumentException if readings are not valid.
130 */
131 public PROMedSRobustRssiRadioSourceEstimator2D(final List<? extends RssiReadingLocated<S, Point2D>> readings) {
132 super(readings);
133 }
134
135 /**
136 * Constructor.
137 *
138 * @param listener listener in charge of attending events raised by this instance.
139 */
140 public PROMedSRobustRssiRadioSourceEstimator2D(final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
141 this.listener = listener;
142 }
143
144 /**
145 * Constructor.
146 * Sets signal readings belonging to the same radio source.
147 *
148 * @param readings signal readings belonging to the same radio source.
149 * @param listener listener in charge of attending events raised by this instance.
150 * @throws IllegalArgumentException if readings are not valid.
151 */
152 public PROMedSRobustRssiRadioSourceEstimator2D(
153 final List<? extends RssiReadingLocated<S, Point2D>> readings,
154 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
155 super(readings, listener);
156 }
157
158 /**
159 * Constructor.
160 * Sets signal readings belonging to the same radio source.
161 *
162 * @param readings signal readings belonging to the same radio source.
163 * @param initialPosition initial position to start the estimation of access
164 * point position.
165 * @throws IllegalArgumentException if readings are not valid.
166 */
167 public PROMedSRobustRssiRadioSourceEstimator2D(
168 final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition) {
169 super(readings, initialPosition);
170 }
171
172 /**
173 * Constructor.
174 *
175 * @param initialPosition initial position to start the estimation of access
176 * point position.
177 */
178 public PROMedSRobustRssiRadioSourceEstimator2D(final Point2D initialPosition) {
179 super(initialPosition);
180 }
181
182 /**
183 * Constructor.
184 *
185 * @param initialPosition initial position to start the estimation of access
186 * point position.
187 * @param listener listener in charge of attending events raised by this instance.
188 */
189 public PROMedSRobustRssiRadioSourceEstimator2D(
190 final Point2D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
191 super(initialPosition, listener);
192 }
193
194 /**
195 * Constructor.
196 * Sets signal readings belonging to the same radio source.
197 *
198 * @param readings signal readings belonging to the same radio source.
199 * @param initialPosition initial position to start the estimation of radio
200 * source position.
201 * @param listener listener in charge of attending events raised by this instance.
202 * @throws IllegalArgumentException if readings are not valid.
203 */
204 public PROMedSRobustRssiRadioSourceEstimator2D(
205 final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
206 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
207 super(readings, initialPosition, listener);
208 }
209
210 /**
211 * Constructor.
212 *
213 * @param initialTransmittedPowerdBm initial transmitted power to start the
214 * estimation of radio source transmitted power
215 * (expressed in dBm's)
216 */
217 public PROMedSRobustRssiRadioSourceEstimator2D(final Double initialTransmittedPowerdBm) {
218 super(initialTransmittedPowerdBm);
219 }
220
221 /**
222 * Constructor.
223 * Sets signal readings belonging to the same radio source.
224 *
225 * @param readings signal readings belonging to the same radio source.
226 * @param initialTransmittedPowerdBm initial transmitted power to start the
227 * estimation of radio source transmitted power
228 * (expressed in dBm's)
229 * @throws IllegalArgumentException if readings are not valid.
230 */
231 public PROMedSRobustRssiRadioSourceEstimator2D(
232 final List<? extends RssiReadingLocated<S, Point2D>> readings, final Double initialTransmittedPowerdBm) {
233 super(readings, initialTransmittedPowerdBm);
234 }
235
236 /**
237 * Constructor.
238 *
239 * @param initialTransmittedPowerdBm initial transmitted power to start the
240 * estimation of radio source transmitted power
241 * (expressed in dBm's)
242 * @param listener listener in charge of attending events raised by this instance.
243 */
244 public PROMedSRobustRssiRadioSourceEstimator2D(
245 final Double initialTransmittedPowerdBm,
246 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
247 super(initialTransmittedPowerdBm, listener);
248 }
249
250 /**
251 * Constructor.
252 * Sets signal readings belonging to the same radio source.
253 *
254 * @param readings signal readings belonging to the same radio source.
255 * @param initialTransmittedPowerdBm initial transmitted power to start the
256 * estimation of radio source transmitted power
257 * (expressed in dBm's)
258 * @param listener listener in charge of attending events raised by this instance.
259 * @throws IllegalArgumentException if readings are not valid.
260 */
261 public PROMedSRobustRssiRadioSourceEstimator2D(
262 final List<? extends RssiReadingLocated<S, Point2D>> readings, final Double initialTransmittedPowerdBm,
263 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
264 super(readings, initialTransmittedPowerdBm, listener);
265 }
266
267 /**
268 * Constructor.
269 * Sets signal readings belonging to the same radio source.
270 *
271 * @param readings signal readings belonging to the same radio source.
272 * @param initialPosition initial position to start the estimation of access
273 * point position.
274 * @param initialTransmittedPowerdBm initial transmitted power to start the
275 * estimation of radio source transmitted power
276 * (expressed in dBm's).
277 * @throws IllegalArgumentException if readings are not valid.
278 */
279 public PROMedSRobustRssiRadioSourceEstimator2D(
280 final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
281 final Double initialTransmittedPowerdBm) {
282 super(readings, initialPosition, initialTransmittedPowerdBm);
283 }
284
285 /**
286 * Constructor.
287 *
288 * @param initialPosition initial position to start the estimation of radio
289 * source position.
290 * @param initialTransmittedPowerdBm initial transmitted power to start the
291 * estimation of radio source transmitted power
292 * (expressed in dBm's).
293 */
294 public PROMedSRobustRssiRadioSourceEstimator2D(
295 final Point2D initialPosition, final Double initialTransmittedPowerdBm) {
296 super(initialPosition, initialTransmittedPowerdBm);
297 }
298
299 /**
300 * Constructor.
301 *
302 * @param initialPosition initial position to start the estimation of radio
303 * source position.
304 * @param initialTransmittedPowerdBm initial transmitted power to start the
305 * estimation of radio source transmitted power
306 * (expressed in dBm's).
307 * @param listener in charge of attending events raised by this instance.
308 */
309 public PROMedSRobustRssiRadioSourceEstimator2D(
310 final Point2D initialPosition, final Double initialTransmittedPowerdBm,
311 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
312 super(initialPosition, initialTransmittedPowerdBm, listener);
313 }
314
315 /**
316 * Constructor.
317 * Sets signal readings belonging to the same radio source.
318 *
319 * @param readings Wi-Fi signal readings belonging to the same radio source.
320 * @param initialPosition initial position to start the estimation of radio
321 * source position.
322 * @param initialTransmittedPowerdBm initial transmitted power to start the
323 * estimation of radio source transmitted power
324 * (expressed in dBm's).
325 * @param listener listener in charge of attending events raised by this instance.
326 * @throws IllegalArgumentException if readings are not valid.
327 */
328 public PROMedSRobustRssiRadioSourceEstimator2D(
329 final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
330 Double initialTransmittedPowerdBm, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
331 super(readings, initialPosition, initialTransmittedPowerdBm, listener);
332 }
333
334 /**
335 * Constructor.
336 * Sets signal readings belonging to the same radio source.
337 *
338 * @param readings signal readings belonging to the same radio source.
339 * @param initialPosition initial position to start the estimation of radio
340 * source position.
341 * @param initialTransmittedPowerdBm initial transmitted power to start the
342 * estimation of radio source transmitted power
343 * (expressed in dBm's).
344 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
345 * @throws IllegalArgumentException if readings are not valid.
346 */
347 public PROMedSRobustRssiRadioSourceEstimator2D(
348 final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
349 Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
350 super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
351 }
352
353 /**
354 * Constructor.
355 *
356 * @param initialPosition initial position to start the estimation of radio
357 * source position.
358 * @param initialTransmittedPowerdBm initial transmitted power to start the
359 * estimation of radio source transmitted power
360 * (expressed in dBm's).
361 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
362 */
363 public PROMedSRobustRssiRadioSourceEstimator2D(
364 final Point2D initialPosition, final Double initialTransmittedPowerdBm,
365 final double initialPathLossExponent) {
366 super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
367 }
368
369 /**
370 * Constructor.
371 *
372 * @param initialPosition initial position to start the estimation of radio
373 * source position.
374 * @param initialTransmittedPowerdBm initial transmitted power to start the
375 * estimation of radio source transmitted power
376 * (expressed in dBm's).
377 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
378 * @param listener listener in charge of attending events raised by this instance.
379 */
380 public PROMedSRobustRssiRadioSourceEstimator2D(
381 final Point2D initialPosition, final Double initialTransmittedPowerdBm,
382 final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
383 super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
384 }
385
386 /**
387 * Constructor.
388 * Sets signal readings belonging to the same radio source.
389 *
390 * @param readings signal readings belonging to the same radio source.
391 * @param initialPosition initial position to start the estimation of radio
392 * source position.
393 * @param initialTransmittedPowerdBm initial transmitted power to start the
394 * estimation of radio source transmitted power
395 * (expressed in dBm's).
396 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
397 * @param listener listener in charge of attending events raised by this instance.
398 * @throws IllegalArgumentException if readings are not valid.
399 */
400 public PROMedSRobustRssiRadioSourceEstimator2D(
401 final List<? extends RssiReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
402 final Double initialTransmittedPowerdBm, final double initialPathLossExponent,
403 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
404 super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
405 }
406
407 /**
408 * Constructor.
409 *
410 * @param qualityScores quality scores corresponding to each provided
411 * sample. The larger the score value the better
412 * the quality of the sample.
413 * @throws IllegalArgumentException if quality scores is null, or length
414 * of quality scores is less than required minimum.
415 */
416 public PROMedSRobustRssiRadioSourceEstimator2D(final double[] qualityScores) {
417 super();
418 internalSetQualityScores(qualityScores);
419 }
420
421 /**
422 * Constructor.
423 * Sets signal readings belonging to the same radio source.
424 *
425 * @param qualityScores quality scores corresponding to each provided
426 * sample. The larger the score value the better
427 * the quality of the sample.
428 * @param readings signal readings belonging to the same radio source.
429 * @throws IllegalArgumentException if readings are not valid, quality scores
430 * is null, or length of quality scores is less than required minimum.
431 */
432 public PROMedSRobustRssiRadioSourceEstimator2D(
433 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings) {
434 super(readings);
435 internalSetQualityScores(qualityScores);
436 }
437
438 /**
439 * Constructor.
440 *
441 * @param qualityScores quality scores corresponding to each provided
442 * sample. The larger the score value the better
443 * the quality of the sample.
444 * @param listener listener in charge of attending events raised by this instance.
445 * @throws IllegalArgumentException if quality scores is null, or length
446 * of quality scores is less than required minimum.
447 */
448 public PROMedSRobustRssiRadioSourceEstimator2D(
449 final double[] qualityScores, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
450 super(listener);
451 internalSetQualityScores(qualityScores);
452 }
453
454 /**
455 * Constructor.
456 * Sets signal readings belonging to the same radio source.
457 *
458 * @param qualityScores quality scores corresponding to each provided
459 * sample. The larger the score value the better
460 * the quality of the sample.
461 * @param readings signal readings belonging to the same radio source.
462 * @param listener listener in charge of attending events raised by this instance.
463 * @throws IllegalArgumentException if readings are not valid, quality scores
464 * is null, or length of quality scores is less than required minimum.
465 */
466 public PROMedSRobustRssiRadioSourceEstimator2D(
467 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
468 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
469 super(readings, listener);
470 internalSetQualityScores(qualityScores);
471 }
472
473 /**
474 * Constructor.
475 * Sets signal readings belonging to the same radio source.
476 *
477 * @param qualityScores quality scores corresponding to each provided
478 * sample. The larger the score value the better
479 * the quality of the sample.
480 * @param readings signal readings belonging to the same radio source.
481 * @param initialPosition initial position to start the estimation of radio
482 * source position.
483 * @throws IllegalArgumentException if readings are not valid, quality scores
484 * is null, or length of quality scores is less than required minimum.
485 */
486 public PROMedSRobustRssiRadioSourceEstimator2D(
487 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
488 final Point2D initialPosition) {
489 super(readings, initialPosition);
490 internalSetQualityScores(qualityScores);
491 }
492
493 /**
494 * Constructor.
495 *
496 * @param qualityScores quality scores corresponding to each provided
497 * sample. The larger the score value the better
498 * the quality of the sample.
499 * @param initialPosition initial position to start the estimation of radio
500 * source position.
501 */
502 public PROMedSRobustRssiRadioSourceEstimator2D(final double[] qualityScores, final Point2D initialPosition) {
503 super(initialPosition);
504 internalSetQualityScores(qualityScores);
505 }
506
507 /**
508 * Constructor.
509 *
510 * @param qualityScores quality scores corresponding to each provided
511 * sample. The larger the score value the better
512 * the quality of the sample.
513 * @param initialPosition initial position to start the estimation of radio
514 * source position.
515 * @param listener listener in charge of attending events raised by this instance.
516 * @throws IllegalArgumentException if quality scores is null, or length
517 * of quality scores is less than required minimum.
518 */
519 public PROMedSRobustRssiRadioSourceEstimator2D(
520 final double[] qualityScores, final Point2D initialPosition,
521 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
522 super(initialPosition, listener);
523 internalSetQualityScores(qualityScores);
524 }
525
526 /**
527 * Constructor.
528 * Sets signal readings belonging to the same radio source.
529 *
530 * @param qualityScores quality scores corresponding to each provided
531 * sample. The larger the score value the better
532 * the quality of the sample.
533 * @param readings signal readings belonging to the same radio source.
534 * @param initialPosition initial position to start the estimation of radio
535 * source position.
536 * @param listener listener in charge of attending events raised by this instance.
537 * @throws IllegalArgumentException if readings are not valid, quality scores
538 * is null, or length of quality scores is less than required minimum.
539 */
540 public PROMedSRobustRssiRadioSourceEstimator2D(
541 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
542 final Point2D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
543 super(readings, initialPosition, listener);
544 internalSetQualityScores(qualityScores);
545 }
546
547 /**
548 * Constructor.
549 *
550 * @param qualityScores quality scores corresponding to each provided
551 * sample. The larger the score value the better
552 * the quality of the sample.
553 * @param initialTransmittedPowerdBm initial transmitted power to start the
554 * estimation of radio source transmitted power
555 * (expressed in dBm's)
556 * @throws IllegalArgumentException if quality scores is null, or length
557 * of quality scores is less than required minimum.
558 */
559 public PROMedSRobustRssiRadioSourceEstimator2D(
560 final double[] qualityScores, final Double initialTransmittedPowerdBm) {
561 super(initialTransmittedPowerdBm);
562 internalSetQualityScores(qualityScores);
563 }
564
565 /**
566 * Constructor.
567 * Sets signal readings belonging to the same radio source.
568 *
569 * @param qualityScores quality scores corresponding to each provided
570 * sample. The larger the score value the better
571 * the quality of the sample.
572 * @param readings signal readings belonging to the same radio source.
573 * @param initialTransmittedPowerdBm initial transmitted power to start the
574 * estimation of radio source transmitted power
575 * (expressed in dBm's)
576 * @throws IllegalArgumentException if readings are not valid, quality scores
577 * is null, or length of quality scores is less than required minimum.
578 */
579 public PROMedSRobustRssiRadioSourceEstimator2D(
580 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
581 final Double initialTransmittedPowerdBm) {
582 super(readings, initialTransmittedPowerdBm);
583 internalSetQualityScores(qualityScores);
584 }
585
586 /**
587 * Constructor.
588 *
589 * @param qualityScores quality scores corresponding to each provided
590 * sample. The larger the score value the better
591 * the quality of the sample.
592 * @param initialTransmittedPowerdBm initial transmitted power to start the
593 * estimation of radio source transmitted power
594 * (expressed in dBm's)
595 * @param listener listener in charge of attending events raised by this instance.
596 * @throws IllegalArgumentException if quality scores is null, or length
597 * of quality scores is less than required minimum.
598 */
599 public PROMedSRobustRssiRadioSourceEstimator2D(
600 final double[] qualityScores, final Double initialTransmittedPowerdBm,
601 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
602 super(initialTransmittedPowerdBm, listener);
603 internalSetQualityScores(qualityScores);
604 }
605
606 /**
607 * Constructor.
608 * Sets signal readings belonging to the same radio source.
609 *
610 * @param qualityScores quality scores corresponding to each provided
611 * sample. The larger the score value the better
612 * the quality of the sample.
613 * @param readings signal readings belonging to the same radio source.
614 * @param initialTransmittedPowerdBm initial transmitted power to start the
615 * estimation of radio source transmitted power
616 * (expressed in dBm's)
617 * @param listener listener in charge of attending events raised by this instance.
618 * @throws IllegalArgumentException if readings are not valid, quality scores
619 * is null, or length of quality scores is less than required minimum.
620 */
621 public PROMedSRobustRssiRadioSourceEstimator2D(
622 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
623 final Double initialTransmittedPowerdBm,
624 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
625 super(readings, initialTransmittedPowerdBm, listener);
626 internalSetQualityScores(qualityScores);
627 }
628
629 /**
630 * Constructor.
631 * Sets signal readings belonging to the same radio source.
632 *
633 * @param qualityScores quality scores corresponding to each provided
634 * sample. The larger the score value the better
635 * the quality of the sample.
636 * @param readings signal readings belonging to the same radio source.
637 * @param initialPosition initial position to start the estimation of radio
638 * source position.
639 * @param initialTransmittedPowerdBm initial transmitted power to start the
640 * estimation of radio source transmitted power
641 * (expressed in dBm's).
642 * @throws IllegalArgumentException if readings are not valid, quality scores
643 * is null, or length of quality scores is less than required minimum.
644 */
645 public PROMedSRobustRssiRadioSourceEstimator2D(
646 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
647 final Point2D initialPosition, final Double initialTransmittedPowerdBm) {
648 super(readings, initialPosition, initialTransmittedPowerdBm);
649 internalSetQualityScores(qualityScores);
650 }
651
652 /**
653 * Constructor.
654 *
655 * @param qualityScores quality scores corresponding to each provided
656 * sample. The larger the score value the better
657 * the quality of the sample.
658 * @param initialPosition initial position to start the estimation of radio
659 * source position.
660 * @param initialTransmittedPowerdBm initial transmitted power to start the
661 * estimation of radio source transmitted power
662 * (expressed in dBm's).
663 * @throws IllegalArgumentException if quality scores is null, or length
664 * of quality scores is less than required minimum.
665 */
666 public PROMedSRobustRssiRadioSourceEstimator2D(
667 final double[] qualityScores, final Point2D initialPosition, final Double initialTransmittedPowerdBm) {
668 super(initialPosition, initialTransmittedPowerdBm);
669 internalSetQualityScores(qualityScores);
670 }
671
672 /**
673 * Constructor.
674 *
675 * @param qualityScores quality scores corresponding to each provided
676 * sample. The larger the score value the better
677 * the quality of the sample.
678 * @param initialPosition initial position to start the estimation of radio
679 * source position.
680 * @param initialTransmittedPowerdBm initial transmitted power to start the
681 * estimation of radio source transmitted power
682 * (expressed in dBm's).
683 * @param listener in charge of attending events raised by this instance.
684 * @throws IllegalArgumentException if quality scores is null, or length
685 * of quality scores is less than required minimum.
686 */
687 public PROMedSRobustRssiRadioSourceEstimator2D(
688 final double[] qualityScores, final Point2D initialPosition, final Double initialTransmittedPowerdBm,
689 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
690 super(initialPosition, initialTransmittedPowerdBm, listener);
691 internalSetQualityScores(qualityScores);
692 }
693
694 /**
695 * Constructor.
696 * Sets signal readings belonging to the same radio source.
697 *
698 * @param qualityScores quality scores corresponding to each provided
699 * sample. The larger the score value the better
700 * the quality of the sample.
701 * @param readings signal readings belonging to the same radio source.
702 * @param initialPosition initial position to start the estimation of radio
703 * source position.
704 * @param initialTransmittedPowerdBm initial transmitted power to start the
705 * estimation of radio source transmitted power
706 * (expressed in dBm's).
707 * @param listener listener in charge of attending events raised by this instance.
708 * @throws IllegalArgumentException if readings are not valid, quality scores
709 * is null, or length of quality scores is less than required minimum.
710 */
711 public PROMedSRobustRssiRadioSourceEstimator2D(
712 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
713 final Point2D initialPosition, final Double initialTransmittedPowerdBm,
714 final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
715 super(readings, initialPosition, initialTransmittedPowerdBm, listener);
716 internalSetQualityScores(qualityScores);
717 }
718
719 /**
720 * Constructor.
721 * Sets signal readings belonging to the same radio source.
722 *
723 * @param qualityScores quality scores corresponding to each provided
724 * sample. The larger the score value the better
725 * the quality of the sample.
726 * @param readings signal readings belonging to the same radio source.
727 * @param initialPosition initial position to start the estimation of radio
728 * source position.
729 * @param initialTransmittedPowerdBm initial transmitted power to start the
730 * estimation of radio source transmitted power
731 * (expressed in dBm's).
732 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
733 * @throws IllegalArgumentException if readings are not valid, quality scores
734 * is null, or length of quality scores is less than required minimum.
735 */
736 public PROMedSRobustRssiRadioSourceEstimator2D(
737 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
738 final Point2D initialPosition, final Double initialTransmittedPowerdBm,
739 final double initialPathLossExponent) {
740 super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
741 internalSetQualityScores(qualityScores);
742 }
743
744 /**
745 * Constructor.
746 *
747 * @param qualityScores quality scores corresponding to each provided
748 * sample. The larger the score value the better
749 * the quality of the sample.
750 * @param initialPosition initial position to start the estimation of radio
751 * source position.
752 * @param initialTransmittedPowerdBm initial transmitted power to start the
753 * estimation of radio source transmitted power
754 * (expressed in dBm's).
755 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
756 */
757 public PROMedSRobustRssiRadioSourceEstimator2D(
758 final double[] qualityScores, final Point2D initialPosition, final Double initialTransmittedPowerdBm,
759 final double initialPathLossExponent) {
760 super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
761 internalSetQualityScores(qualityScores);
762 }
763
764 /**
765 * Constructor.
766 *
767 * @param qualityScores quality scores corresponding to each provided
768 * sample. The larger the score value the better
769 * the quality of the sample.
770 * @param initialPosition initial position to start the estimation of radio
771 * source position.
772 * @param initialTransmittedPowerdBm initial transmitted power to start the
773 * estimation of radio source transmitted power
774 * (expressed in dBm's).
775 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
776 * @param listener listener in charge of attending events raised by this instance.
777 */
778 public PROMedSRobustRssiRadioSourceEstimator2D(
779 final double[] qualityScores, final Point2D initialPosition, final Double initialTransmittedPowerdBm,
780 final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
781 super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
782 internalSetQualityScores(qualityScores);
783 }
784
785 /**
786 * Constructor.
787 * Sets signal readings belonging to the same radio source.
788 *
789 * @param qualityScores quality scores corresponding to each provided
790 * sample. The larger the score value the better
791 * the quality of the sample.
792 * @param readings signal readings belonging to the same radio source.
793 * @param initialPosition initial position to start the estimation of radio
794 * source position.
795 * @param initialTransmittedPowerdBm initial transmitted power to start the
796 * estimation of radio source transmitted power
797 * (expressed in dBm's).
798 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
799 * @param listener listener in charge of attending events raised by this instance.
800 * @throws IllegalArgumentException if readings are not valid, quality scores
801 * is null, or length of quality scores is less than required minimum.
802 */
803 public PROMedSRobustRssiRadioSourceEstimator2D(
804 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point2D>> readings,
805 final Point2D initialPosition, final Double initialTransmittedPowerdBm,
806 final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point2D> listener) {
807 super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
808 internalSetQualityScores(qualityScores);
809 }
810
811 /**
812 * Returns threshold to be used to keep the algorithm iterating in case that
813 * best estimated threshold using median of residuals is not small enough.
814 * Once a solution is found that generates a threshold below this value, the
815 * algorithm will stop.
816 * The stop threshold can be used to prevent the LMedS algorithm to iterate
817 * too many times in cases where samples have a very similar accuracy.
818 * For instance, in cases where proportion of outliers is very small (close
819 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
820 * iterate for a long time trying to find the best solution when indeed
821 * there is no need to do that if a reasonable threshold has already been
822 * reached.
823 * Because of this behaviour the stop threshold can be set to a value much
824 * lower than the one typically used in RANSAC, and yet the algorithm could
825 * still produce even smaller thresholds in estimated results.
826 *
827 * @return stop threshold to stop the algorithm prematurely when a certain
828 * accuracy has been reached.
829 */
830 public double getStopThreshold() {
831 return stopThreshold;
832 }
833
834 /**
835 * Sets threshold to be used to keep the algorithm iterating in case that
836 * best estimated threshold using median of residuals is not small enough.
837 * Once a solution is found that generates a threshold below this value,
838 * the algorithm will stop.
839 * The stop threshold can be used to prevent the LMedS algorithm to iterate
840 * too many times in cases where samples have a very similar accuracy.
841 * For instance, in cases where proportion of outliers is very small (close
842 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
843 * iterate for a long time trying to find the best solution when indeed
844 * there is no need to do that if a reasonable threshold has already been
845 * reached.
846 * Because of this behaviour the stop threshold can be set to a value much
847 * lower than the one typically used in RANSAC, and yet the algorithm could
848 * still produce even smaller thresholds in estimated results.
849 *
850 * @param stopThreshold stop threshold to stop the algorithm prematurely
851 * when a certain accuracy has been reached.
852 * @throws IllegalArgumentException if provided value is zero or negative.
853 * @throws LockedException if this solver is locked.
854 */
855 public void setStopThreshold(final double stopThreshold) throws LockedException {
856 if (isLocked()) {
857 throw new LockedException();
858 }
859 if (stopThreshold <= MIN_STOP_THRESHOLD) {
860 throw new IllegalArgumentException();
861 }
862
863 this.stopThreshold = stopThreshold;
864 }
865
866 /**
867 * Returns quality scores corresponding to each pair of
868 * positions and distances (i.e. sample).
869 * The larger the score value the better the quality of the sample.
870 * This implementation always returns null.
871 * Subclasses using quality scores must implement proper behavior.
872 *
873 * @return quality scores corresponding to each sample.
874 */
875 @Override
876 public double[] getQualityScores() {
877 return qualityScores;
878 }
879
880 /**
881 * Sets quality scores corresponding to each pair of positions and
882 * distances (i.e. sample).
883 * The larger the score value the better the quality of the sample.
884 * This implementation makes no action.
885 * Subclasses using quality scores must implement proper behaviour.
886 *
887 * @param qualityScores quality scores corresponding to each pair of
888 * matched points.
889 * @throws IllegalArgumentException if provided quality scores length
890 * is smaller than minimum required samples.
891 * @throws LockedException if robust solver is locked because an
892 * estimation is already in progress.
893 */
894 @Override
895 public void setQualityScores(final double[] qualityScores) throws LockedException {
896 if (isLocked()) {
897 throw new LockedException();
898 }
899 internalSetQualityScores(qualityScores);
900 }
901
902 /**
903 * Indicates whether solver is ready to find a solution.
904 *
905 * @return true if solver is ready, false otherwise.
906 */
907 @Override
908 public boolean isReady() {
909 return super.isReady() && qualityScores != null && qualityScores.length == readings.size();
910 }
911
912 /**
913 * Robustly estimates position, transmitted power and path-loss exponent for a
914 * radio source.
915 *
916 * @throws LockedException if instance is busy during estimation.
917 * @throws NotReadyException if estimator is not ready.
918 * @throws RobustEstimatorException if estimation fails for any reason
919 * (i.e. numerical instability, no solution available, etc).
920 */
921 @Override
922 public void estimate() throws LockedException, NotReadyException, RobustEstimatorException {
923 if (isLocked()) {
924 throw new LockedException();
925 }
926 if (!isReady()) {
927 throw new NotReadyException();
928 }
929
930 final var innerEstimator = new PROMedSRobustEstimator<>(
931 new PROMedSRobustEstimatorListener<Solution<Point2D>>() {
932
933 @Override
934 public double[] getQualityScores() {
935 return qualityScores;
936 }
937
938 @Override
939 public double getThreshold() {
940 return stopThreshold;
941 }
942
943 @Override
944 public int getTotalSamples() {
945 return readings.size();
946 }
947
948 @Override
949 public int getSubsetSize() {
950 return Math.max(preliminarySubsetSize, getMinReadings());
951 }
952
953 @Override
954 public void estimatePreliminarSolutions(
955 final int[] samplesIndices, final List<Solution<Point2D>> solutions) {
956 solvePreliminarySolutions(samplesIndices, solutions);
957 }
958
959 @Override
960 public double computeResidual(final Solution<Point2D> currentEstimation, int i) {
961 return residual(currentEstimation, i);
962 }
963
964 @Override
965 public boolean isReady() {
966 return PROMedSRobustRssiRadioSourceEstimator2D.this.isReady();
967 }
968
969 @Override
970 public void onEstimateStart(final RobustEstimator<Solution<Point2D>> estimator) {
971 // no action needed
972 }
973
974 @Override
975 public void onEstimateEnd(final RobustEstimator<Solution<Point2D>> estimator) {
976 // no action needed
977 }
978
979 @Override
980 public void onEstimateNextIteration(
981 final RobustEstimator<Solution<Point2D>> estimator, final int iteration) {
982 if (listener != null) {
983 listener.onEstimateNextIteration(
984 PROMedSRobustRssiRadioSourceEstimator2D.this, iteration);
985 }
986 }
987
988 @Override
989 public void onEstimateProgressChange(
990 final RobustEstimator<Solution<Point2D>> estimator, final float progress) {
991 if (listener != null) {
992 listener.onEstimateProgressChange(
993 PROMedSRobustRssiRadioSourceEstimator2D.this, progress);
994 }
995 }
996 });
997
998 try {
999 locked = true;
1000
1001 if (listener != null) {
1002 listener.onEstimateStart(this);
1003 }
1004
1005 inliersData = null;
1006
1007 // inlier thresholds are disable to obtain a less restrictive amount of inliers
1008 innerEstimator.setUseInlierThresholds(false);
1009
1010 innerEstimator.setConfidence(confidence);
1011 innerEstimator.setMaxIterations(maxIterations);
1012 innerEstimator.setProgressDelta(progressDelta);
1013 final var result = innerEstimator.estimate();
1014 inliersData = innerEstimator.getInliersData();
1015 attemptRefine(result);
1016
1017 if (listener != null) {
1018 listener.onEstimateEnd(this);
1019 }
1020
1021 } catch (final com.irurueta.numerical.LockedException e) {
1022 throw new LockedException(e);
1023 } catch (final com.irurueta.numerical.NotReadyException e) {
1024 throw new NotReadyException(e);
1025 } finally {
1026 locked = false;
1027 }
1028 }
1029
1030 /**
1031 * Returns method being used for robust estimation.
1032 *
1033 * @return method being used for robust estimation.
1034 */
1035 @Override
1036 public RobustEstimatorMethod getMethod() {
1037 return RobustEstimatorMethod.PROMEDS;
1038 }
1039
1040 /**
1041 * Sets quality scores corresponding to each provided sample.
1042 * This method is used internally and does not check whether instance is
1043 * locked or not.
1044 *
1045 * @param qualityScores quality scores to be set.
1046 * @throws IllegalArgumentException if provided quality scores length
1047 * is smaller than 3 samples.
1048 */
1049 private void internalSetQualityScores(final double[] qualityScores) {
1050 if (qualityScores == null || qualityScores.length < getMinReadings()) {
1051 throw new IllegalArgumentException();
1052 }
1053
1054 this.qualityScores = qualityScores;
1055 }
1056 }