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.Point3D;
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 3D 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 PROMedSRobustRssiRadioSourceEstimator3D<S extends RadioSource> extends
67 RobustRssiRadioSourceEstimator3D<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 PROMedSRobustRssiRadioSourceEstimator3D() {
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 PROMedSRobustRssiRadioSourceEstimator3D(final List<? extends RssiReadingLocated<S, Point3D>> 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 PROMedSRobustRssiRadioSourceEstimator3D(final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROMedSRobustRssiRadioSourceEstimator3D(
153 final List<? extends RssiReadingLocated<S, Point3D>> readings,
154 final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 radio
164 * source position.
165 * @throws IllegalArgumentException if readings are not valid.
166 */
167 public PROMedSRobustRssiRadioSourceEstimator3D(
168 final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D 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 PROMedSRobustRssiRadioSourceEstimator3D(final Point3D initialPosition) {
179 super(initialPosition);
180 }
181
182 /**
183 * Constructor.
184 *
185 * @param initialPosition initial position to start the estimation of radio
186 * source position.
187 * @param listener listener in charge of attending events raised by this instance.
188 */
189 public PROMedSRobustRssiRadioSourceEstimator3D(
190 final Point3D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROMedSRobustRssiRadioSourceEstimator3D(
205 final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
206 final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
207 super(readings, initialPosition, listener);
208 }
209
210 /**
211 * Constructor.
212 *
213 * @param initialTransmittedPowerdBm initial transmitted power to start the
214 * estimation of access point transmitted power
215 * (expressed in dBm's)
216 */
217 public PROMedSRobustRssiRadioSourceEstimator3D(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 PROMedSRobustRssiRadioSourceEstimator3D(
232 final List<? extends RssiReadingLocated<S, Point3D>> 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 PROMedSRobustRssiRadioSourceEstimator3D(
245 final Double initialTransmittedPowerdBm,
246 final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 PROMedSRobustRssiRadioSourceEstimator3D(
262 final List<? extends RssiReadingLocated<S, Point3D>> readings, final Double initialTransmittedPowerdBm,
263 final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 radio
273 * source 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 PROMedSRobustRssiRadioSourceEstimator3D(
280 final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D 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 PROMedSRobustRssiRadioSourceEstimator3D(
295 final Point3D 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 PROMedSRobustRssiRadioSourceEstimator3D(
310 final Point3D initialPosition, final Double initialTransmittedPowerdBm,
311 final RobustRssiRadioSourceEstimatorListener<S, Point3D> 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 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 PROMedSRobustRssiRadioSourceEstimator3D(
329 final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
330 final Double initialTransmittedPowerdBm,
331 final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
332 super(readings, initialPosition, initialTransmittedPowerdBm, listener);
333 }
334
335 /**
336 * Constructor.
337 * Sets signal readings belonging to the same radio source.
338 *
339 * @param readings signal readings belonging to the same radio source.
340 * @param initialPosition initial position to start the estimation of radio
341 * source position.
342 * @param initialTransmittedPowerdBm initial transmitted power to start the
343 * estimation of radio source transmitted power
344 * (expressed in dBm's).
345 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
346 * @throws IllegalArgumentException if readings are not valid.
347 */
348 public PROMedSRobustRssiRadioSourceEstimator3D(
349 final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
350 final Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
351 super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
352 }
353
354 /**
355 * Constructor.
356 *
357 * @param initialPosition initial position to start the estimation of radio
358 * source position.
359 * @param initialTransmittedPowerdBm initial transmitted power to start the
360 * estimation of radio source transmitted power
361 * (expressed in dBm's).
362 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
363 */
364 public PROMedSRobustRssiRadioSourceEstimator3D(
365 final Point3D initialPosition, final Double initialTransmittedPowerdBm,
366 final double initialPathLossExponent) {
367 super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
368 }
369
370 /**
371 * Constructor.
372 *
373 * @param initialPosition initial position to start the estimation of radio
374 * source position.
375 * @param initialTransmittedPowerdBm initial transmitted power to start the
376 * estimation of radio source transmitted power
377 * (expressed in dBm's).
378 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
379 * @param listener listener in charge of attending events raised by this instance.
380 */
381 public PROMedSRobustRssiRadioSourceEstimator3D(
382 final Point3D initialPosition, final Double initialTransmittedPowerdBm,
383 final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
384 super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
385 }
386
387 /**
388 * Constructor.
389 * Sets signal readings belonging to the same radio source.
390 *
391 * @param readings signal readings belonging to the same radio source.
392 * @param initialPosition initial position to start the estimation of radio
393 * source position.
394 * @param initialTransmittedPowerdBm initial transmitted power to start the
395 * estimation of radio source transmitted power
396 * (expressed in dBm's).
397 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
398 * @param listener listener in charge of attending events raised by this instance.
399 * @throws IllegalArgumentException if readings are not valid.
400 */
401 public PROMedSRobustRssiRadioSourceEstimator3D(
402 final List<? extends RssiReadingLocated<S, Point3D>> readings,
403 final Point3D initialPosition, final Double initialTransmittedPowerdBm,
404 final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
405 super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
406 }
407
408 /**
409 * Constructor.
410 *
411 * @param qualityScores quality scores corresponding to each provided
412 * sample. The larger the score value the better
413 * the quality of the sample.
414 * @throws IllegalArgumentException if quality scores is null, or length
415 * of quality scores is less than required minimum.
416 */
417 public PROMedSRobustRssiRadioSourceEstimator3D(final double[] qualityScores) {
418 super();
419 internalSetQualityScores(qualityScores);
420 }
421
422 /**
423 * Constructor.
424 * Sets signal readings belonging to the same radio source.
425 *
426 * @param qualityScores quality scores corresponding to each provided
427 * sample. The larger the score value the better
428 * the quality of the sample.
429 * @param readings signal readings belonging to the same radio source.
430 * @throws IllegalArgumentException if readings are not valid, quality scores
431 * is null, or length of quality scores is less than required minimum.
432 */
433 public PROMedSRobustRssiRadioSourceEstimator3D(
434 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings) {
435 super(readings);
436 internalSetQualityScores(qualityScores);
437 }
438
439 /**
440 * Constructor.
441 *
442 * @param qualityScores quality scores corresponding to each provided
443 * sample. The larger the score value the better
444 * the quality of the sample.
445 * @param listener listener in charge of attending events raised by this instance.
446 * @throws IllegalArgumentException if quality scores is null, or length
447 * of quality scores is less than required minimum.
448 */
449 public PROMedSRobustRssiRadioSourceEstimator3D(
450 final double[] qualityScores, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
451 super(listener);
452 internalSetQualityScores(qualityScores);
453 }
454
455 /**
456 * Constructor.
457 * Sets signal readings belonging to the same radio source.
458 *
459 * @param qualityScores quality scores corresponding to each provided
460 * sample. The larger the score value the better
461 * the quality of the sample.
462 * @param readings signal readings belonging to the same radio source.
463 * @param listener listener in charge of attending events raised by this instance.
464 * @throws IllegalArgumentException if readings are not valid, quality scores
465 * is null, or length of quality scores is less than required minimum.
466 */
467 public PROMedSRobustRssiRadioSourceEstimator3D(
468 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
469 final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
470 super(readings, listener);
471 internalSetQualityScores(qualityScores);
472 }
473
474 /**
475 * Constructor.
476 * Sets signal readings belonging to the same radio source.
477 *
478 * @param qualityScores quality scores corresponding to each provided
479 * sample. The larger the score value the better
480 * the quality of the sample.
481 * @param readings signal readings belonging to the same radio source.
482 * @param initialPosition initial position to start the estimation of access
483 * point position.
484 * @throws IllegalArgumentException if readings are not valid, quality scores
485 * is null, or length of quality scores is less than required minimum.
486 */
487 public PROMedSRobustRssiRadioSourceEstimator3D(
488 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
489 final Point3D initialPosition) {
490 super(readings, initialPosition);
491 internalSetQualityScores(qualityScores);
492 }
493
494 /**
495 * Constructor.
496 *
497 * @param qualityScores quality scores corresponding to each provided
498 * sample. The larger the score value the better
499 * the quality of the sample.
500 * @param initialPosition initial position to start the estimation of radio
501 * source position.
502 */
503 public PROMedSRobustRssiRadioSourceEstimator3D(final double[] qualityScores, final Point3D initialPosition) {
504 super(initialPosition);
505 internalSetQualityScores(qualityScores);
506 }
507
508 /**
509 * Constructor.
510 *
511 * @param qualityScores quality scores corresponding to each provided
512 * sample. The larger the score value the better
513 * the quality of the sample.
514 * @param initialPosition initial position to start the estimation of radio
515 * source position.
516 * @param listener listener in charge of attending events raised by this instance.
517 * @throws IllegalArgumentException if quality scores is null, or length
518 * of quality scores is less than required minimum.
519 */
520 public PROMedSRobustRssiRadioSourceEstimator3D(
521 final double[] qualityScores, final Point3D initialPosition,
522 final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
523 super(initialPosition, listener);
524 internalSetQualityScores(qualityScores);
525 }
526
527 /**
528 * Constructor.
529 * Sets signal readings belonging to the same radio source.
530 *
531 * @param qualityScores quality scores corresponding to each provided
532 * sample. The larger the score value the better
533 * the quality of the sample.
534 * @param readings signal readings belonging to the same radio source.
535 * @param initialPosition initial position to start the estimation of radio
536 * source position.
537 * @param listener listener in charge of attending events raised by this instance.
538 * @throws IllegalArgumentException if readings are not valid, quality scores
539 * is null, or length of quality scores is less than required minimum.
540 */
541 public PROMedSRobustRssiRadioSourceEstimator3D(
542 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
543 final Point3D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
544 super(readings, initialPosition, listener);
545 internalSetQualityScores(qualityScores);
546 }
547
548 /**
549 * Constructor.
550 *
551 * @param qualityScores quality scores corresponding to each provided
552 * sample. The larger the score value the better
553 * the quality of the sample.
554 * @param initialTransmittedPowerdBm initial transmitted power to start the
555 * estimation of access point transmitted power
556 * (expressed in dBm's)
557 * @throws IllegalArgumentException if quality scores is null, or length
558 * of quality scores is less than required minimum.
559 */
560 public PROMedSRobustRssiRadioSourceEstimator3D(
561 final double[] qualityScores, final Double initialTransmittedPowerdBm) {
562 super(initialTransmittedPowerdBm);
563 internalSetQualityScores(qualityScores);
564 }
565
566 /**
567 * Constructor.
568 * Sets signal readings belonging to the same radio source.
569 *
570 * @param qualityScores quality scores corresponding to each provided
571 * sample. The larger the score value the better
572 * the quality of the sample.
573 * @param readings signal readings belonging to the same radio source.
574 * @param initialTransmittedPowerdBm initial transmitted power to start the
575 * estimation of radio source transmitted power
576 * (expressed in dBm's)
577 * @throws IllegalArgumentException if readings are not valid, quality scores
578 * is null, or length of quality scores is less than required minimum.
579 */
580 public PROMedSRobustRssiRadioSourceEstimator3D(
581 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
582 final Double initialTransmittedPowerdBm) {
583 super(readings, initialTransmittedPowerdBm);
584 internalSetQualityScores(qualityScores);
585 }
586
587 /**
588 * Constructor.
589 *
590 * @param qualityScores quality scores corresponding to each provided
591 * sample. The larger the score value the better
592 * the quality of the sample.
593 * @param initialTransmittedPowerdBm initial transmitted power to start the
594 * estimation of access point transmitted power
595 * (expressed in dBm's)
596 * @param listener listener in charge of attending events raised by this instance.
597 * @throws IllegalArgumentException if quality scores is null, or length
598 * of quality scores is less than required minimum.
599 */
600 public PROMedSRobustRssiRadioSourceEstimator3D(
601 final double[] qualityScores, final Double initialTransmittedPowerdBm,
602 final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
603 super(initialTransmittedPowerdBm, listener);
604 internalSetQualityScores(qualityScores);
605 }
606
607 /**
608 * Constructor.
609 * Sets signal readings belonging to the same radio source.
610 *
611 * @param qualityScores quality scores corresponding to each provided
612 * sample. The larger the score value the better
613 * the quality of the sample.
614 * @param readings signal readings belonging to the same radio source.
615 * @param initialTransmittedPowerdBm initial transmitted power to start the
616 * estimation of radio source transmitted power
617 * (expressed in dBm's)
618 * @param listener listener in charge of attending events raised by this instance.
619 * @throws IllegalArgumentException if readings are not valid, quality scores
620 * is null, or length of quality scores is less than required minimum.
621 */
622 public PROMedSRobustRssiRadioSourceEstimator3D(
623 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
624 final Double initialTransmittedPowerdBm,
625 final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
626 super(readings, initialTransmittedPowerdBm, listener);
627 internalSetQualityScores(qualityScores);
628 }
629
630 /**
631 * Constructor.
632 * Sets signal readings belonging to the same radio source.
633 *
634 * @param qualityScores quality scores corresponding to each provided
635 * sample. The larger the score value the better
636 * the quality of the sample.
637 * @param readings signal readings belonging to the same radio source.
638 * @param initialPosition initial position to start the estimation of access
639 * point position.
640 * @param initialTransmittedPowerdBm initial transmitted power to start the
641 * estimation of radio source transmitted power
642 * (expressed in dBm's).
643 * @throws IllegalArgumentException if readings are not valid, quality scores
644 * is null, or length of quality scores is less than required minimum.
645 */
646 public PROMedSRobustRssiRadioSourceEstimator3D(
647 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
648 final Point3D initialPosition, final Double initialTransmittedPowerdBm) {
649 super(readings, initialPosition, initialTransmittedPowerdBm);
650 internalSetQualityScores(qualityScores);
651 }
652
653 /**
654 * Constructor.
655 *
656 * @param qualityScores quality scores corresponding to each provided
657 * sample. The larger the score value the better
658 * the quality of the sample.
659 * @param initialPosition initial position to start the estimation of radio
660 * source position.
661 * @param initialTransmittedPowerdBm initial transmitted power to start the
662 * estimation of radio source transmitted power
663 * (expressed in dBm's).
664 * @throws IllegalArgumentException if quality scores is null, or length
665 * of quality scores is less than required minimum.
666 */
667 public PROMedSRobustRssiRadioSourceEstimator3D(
668 final double[] qualityScores, final Point3D initialPosition, final Double initialTransmittedPowerdBm) {
669 super(initialPosition, initialTransmittedPowerdBm);
670 internalSetQualityScores(qualityScores);
671 }
672
673 /**
674 * Constructor.
675 *
676 * @param qualityScores quality scores corresponding to each provided
677 * sample. The larger the score value the better
678 * the quality of the sample.
679 * @param initialPosition initial position to start the estimation of radio
680 * source position.
681 * @param initialTransmittedPowerdBm initial transmitted power to start the
682 * estimation of radio source transmitted power
683 * (expressed in dBm's).
684 * @param listener in charge of attending events raised by this instance.
685 * @throws IllegalArgumentException if quality scores is null, or length
686 * of quality scores is less than required minimum.
687 */
688 public PROMedSRobustRssiRadioSourceEstimator3D(
689 final double[] qualityScores, final Point3D initialPosition,
690 final Double initialTransmittedPowerdBm,
691 final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
692 super(initialPosition, initialTransmittedPowerdBm, listener);
693 internalSetQualityScores(qualityScores);
694 }
695
696 /**
697 * Constructor.
698 * Sets signal readings belonging to the same radio source.
699 *
700 * @param qualityScores quality scores corresponding to each provided
701 * sample. The larger the score value the better
702 * the quality of the sample.
703 * @param readings signal readings containing to the same radio source.
704 * @param initialPosition initial position to start the estimation of radio
705 * source position.
706 * @param initialTransmittedPowerdBm initial transmitted power to start the
707 * estimation of radio source transmitted power
708 * (expressed in dBm's).
709 * @param listener listener in charge of attending events raised by this instance.
710 * @throws IllegalArgumentException if readings are not valid, quality scores
711 * is null, or length of quality scores is less than required minimum.
712 */
713 public PROMedSRobustRssiRadioSourceEstimator3D(
714 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
715 final Point3D initialPosition, final Double initialTransmittedPowerdBm,
716 final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
717 super(readings, initialPosition, initialTransmittedPowerdBm, listener);
718 internalSetQualityScores(qualityScores);
719 }
720
721 /**
722 * Constructor.
723 * Sets signal readings belonging to the same radio source.
724 *
725 * @param qualityScores quality scores corresponding to each provided
726 * sample. The larger the score value the better
727 * the quality of the sample.
728 * @param readings signal readings belonging to the same radio source.
729 * @param initialPosition initial position to start the estimation of radio
730 * source position.
731 * @param initialTransmittedPowerdBm initial transmitted power to start the
732 * estimation of radio source transmitted power
733 * (expressed in dBm's).
734 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
735 * @throws IllegalArgumentException if readings are not valid, quality scores
736 * is null, or length of quality scores is less than required minimum.
737 */
738 public PROMedSRobustRssiRadioSourceEstimator3D(
739 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
740 final Point3D initialPosition, final Double initialTransmittedPowerdBm,
741 final double initialPathLossExponent) {
742 super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
743 internalSetQualityScores(qualityScores);
744 }
745
746 /**
747 * Constructor.
748 *
749 * @param qualityScores quality scores corresponding to each provided
750 * sample. The larger the score value the better
751 * the quality of the sample.
752 * @param initialPosition initial position to start the estimation of radio
753 * source position.
754 * @param initialTransmittedPowerdBm initial transmitted power to start the
755 * estimation of radio source transmitted power
756 * (expressed in dBm's).
757 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
758 */
759 public PROMedSRobustRssiRadioSourceEstimator3D(
760 final double[] qualityScores, final Point3D initialPosition, final Double initialTransmittedPowerdBm,
761 final double initialPathLossExponent) {
762 super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
763 internalSetQualityScores(qualityScores);
764 }
765
766 /**
767 * Constructor.
768 *
769 * @param qualityScores quality scores corresponding to each provided
770 * sample. The larger the score value the better
771 * the quality of the sample.
772 * @param initialPosition initial position to start the estimation of radio
773 * source position.
774 * @param initialTransmittedPowerdBm initial transmitted power to start the
775 * estimation of radio source transmitted power
776 * (expressed in dBm's).
777 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
778 * @param listener listener in charge of attending events raised by this instance.
779 */
780 public PROMedSRobustRssiRadioSourceEstimator3D(
781 final double[] qualityScores, final Point3D initialPosition, final Double initialTransmittedPowerdBm,
782 final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
783 super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
784 internalSetQualityScores(qualityScores);
785 }
786
787 /**
788 * Constructor.
789 * Sets signal readings belonging to the same radio source.
790 *
791 * @param qualityScores quality scores corresponding to each provided
792 * sample. The larger the score value the better
793 * the quality of the sample.
794 * @param readings signal readings belonging to the same radio source.
795 * @param initialPosition initial position to start the estimation of radio
796 * source position.
797 * @param initialTransmittedPowerdBm initial transmitted power to start the
798 * estimation of radio source transmitted power
799 * (expressed in dBm's).
800 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
801 * @param listener listener in charge of attending events raised by this instance.
802 * @throws IllegalArgumentException if readings are not valid, quality scores
803 * is null, or length of quality scores is less than required minimum.
804 */
805 public PROMedSRobustRssiRadioSourceEstimator3D(
806 final double[] qualityScores, final List<? extends RssiReadingLocated<S, Point3D>> readings,
807 final Point3D initialPosition, final Double initialTransmittedPowerdBm,
808 final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
809 super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
810 internalSetQualityScores(qualityScores);
811 }
812
813 /**
814 * Returns threshold to be used to keep the algorithm iterating in case that
815 * best estimated threshold using median of residuals is not small enough.
816 * Once a solution is found that generates a threshold below this value, the
817 * algorithm will stop.
818 * The stop threshold can be used to prevent the LMedS algorithm to iterate
819 * too many times in cases where samples have a very similar accuracy.
820 * For instance, in cases where proportion of outliers is very small (close
821 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
822 * iterate for a long time trying to find the best solution when indeed
823 * there is no need to do that if a reasonable threshold has already been
824 * reached.
825 * Because of this behaviour the stop threshold can be set to a value much
826 * lower than the one typically used in RANSAC, and yet the algorithm could
827 * still produce even smaller thresholds in estimated results.
828 *
829 * @return stop threshold to stop the algorithm prematurely when a certain
830 * accuracy has been reached.
831 */
832 public double getStopThreshold() {
833 return stopThreshold;
834 }
835
836 /**
837 * Sets threshold to be used to keep the algorithm iterating in case that
838 * best estimated threshold using median of residuals is not small enough.
839 * Once a solution is found that generates a threshold below this value,
840 * the algorithm will stop.
841 * The stop threshold can be used to prevent the LMedS algorithm to iterate
842 * too many times in cases where samples have a very similar accuracy.
843 * For instance, in cases where proportion of outliers is very small (close
844 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
845 * iterate for a long time trying to find the best solution when indeed
846 * there is no need to do that if a reasonable threshold has already been
847 * reached.
848 * Because of this behaviour the stop threshold can be set to a value much
849 * lower than the one typically used in RANSAC, and yet the algorithm could
850 * still produce even smaller thresholds in estimated results.
851 *
852 * @param stopThreshold stop threshold to stop the algorithm prematurely
853 * when a certain accuracy has been reached.
854 * @throws IllegalArgumentException if provided value is zero or negative.
855 * @throws LockedException if this solver is locked.
856 */
857 public void setStopThreshold(final double stopThreshold) throws LockedException {
858 if (isLocked()) {
859 throw new LockedException();
860 }
861 if (stopThreshold <= MIN_STOP_THRESHOLD) {
862 throw new IllegalArgumentException();
863 }
864
865 this.stopThreshold = stopThreshold;
866 }
867
868 /**
869 * Returns quality scores corresponding to each pair of
870 * positions and distances (i.e. sample).
871 * The larger the score value the better the quality of the sample.
872 * This implementation always returns null.
873 * Subclasses using quality scores must implement proper behavior.
874 *
875 * @return quality scores corresponding to each sample.
876 */
877 @Override
878 public double[] getQualityScores() {
879 return qualityScores;
880 }
881
882 /**
883 * Sets quality scores corresponding to each pair of positions and
884 * distances (i.e. sample).
885 * The larger the score value the better the quality of the sample.
886 * This implementation makes no action.
887 * Subclasses using quality scores must implement proper behaviour.
888 *
889 * @param qualityScores quality scores corresponding to each pair of
890 * matched points.
891 * @throws IllegalArgumentException if provided quality scores length
892 * is smaller than minimum required samples.
893 * @throws LockedException if robust solver is locked because an
894 * estimation is already in progress.
895 */
896 @Override
897 public void setQualityScores(final double[] qualityScores) throws LockedException {
898 if (isLocked()) {
899 throw new LockedException();
900 }
901 internalSetQualityScores(qualityScores);
902 }
903
904 /**
905 * Indicates whether solver is ready to find a solution.
906 *
907 * @return true if solver is ready, false otherwise.
908 */
909 @Override
910 public boolean isReady() {
911 return super.isReady() && qualityScores != null && qualityScores.length == readings.size();
912 }
913
914 /**
915 * Robustly estimates position, transmitted power and path-loss exponent for a
916 * radio source.
917 *
918 * @throws LockedException if instance is busy during estimation.
919 * @throws NotReadyException if estimator is not ready.
920 * @throws RobustEstimatorException if estimation fails for any reason
921 * (i.e. numerical instability, no solution available, etc).
922 */
923 @Override
924 public void estimate() throws LockedException, NotReadyException, RobustEstimatorException {
925 if (isLocked()) {
926 throw new LockedException();
927 }
928 if (!isReady()) {
929 throw new NotReadyException();
930 }
931
932 final var innerEstimator = new PROMedSRobustEstimator<>(
933 new PROMedSRobustEstimatorListener<Solution<Point3D>>() {
934
935 @Override
936 public double[] getQualityScores() {
937 return qualityScores;
938 }
939
940 @Override
941 public double getThreshold() {
942 return stopThreshold;
943 }
944
945 @Override
946 public int getTotalSamples() {
947 return readings.size();
948 }
949
950 @Override
951 public int getSubsetSize() {
952 return Math.max(preliminarySubsetSize, getMinReadings());
953 }
954
955 @Override
956 public void estimatePreliminarSolutions(
957 final int[] samplesIndices, final List<Solution<Point3D>> solutions) {
958 solvePreliminarySolutions(samplesIndices, solutions);
959 }
960
961 @Override
962 public double computeResidual(final Solution<Point3D> currentEstimation, final int i) {
963 return residual(currentEstimation, i);
964 }
965
966 @Override
967 public boolean isReady() {
968 return PROMedSRobustRssiRadioSourceEstimator3D.this.isReady();
969 }
970
971 @Override
972 public void onEstimateStart(final RobustEstimator<Solution<Point3D>> estimator) {
973 // no action needed
974 }
975
976 @Override
977 public void onEstimateEnd(final RobustEstimator<Solution<Point3D>> estimator) {
978 // no action needed
979 }
980
981 @Override
982 public void onEstimateNextIteration(
983 final RobustEstimator<Solution<Point3D>> estimator, final int iteration) {
984 if (listener != null) {
985 listener.onEstimateNextIteration(
986 PROMedSRobustRssiRadioSourceEstimator3D.this, iteration);
987 }
988 }
989
990 @Override
991 public void onEstimateProgressChange(
992 final RobustEstimator<Solution<Point3D>> estimator, final float progress) {
993 if (listener != null) {
994 listener.onEstimateProgressChange(
995 PROMedSRobustRssiRadioSourceEstimator3D.this, progress);
996 }
997 }
998 });
999
1000 try {
1001 locked = true;
1002
1003 if (listener != null) {
1004 listener.onEstimateStart(this);
1005 }
1006
1007 inliersData = null;
1008
1009 // inlier thresholds are disable to obtain a less restrictive amount of inliers
1010 innerEstimator.setUseInlierThresholds(false);
1011
1012 innerEstimator.setConfidence(confidence);
1013 innerEstimator.setMaxIterations(maxIterations);
1014 innerEstimator.setProgressDelta(progressDelta);
1015 final var result = innerEstimator.estimate();
1016 inliersData = innerEstimator.getInliersData();
1017 attemptRefine(result);
1018
1019 if (listener != null) {
1020 listener.onEstimateEnd(this);
1021 }
1022
1023 } catch (final com.irurueta.numerical.LockedException e) {
1024 throw new LockedException(e);
1025 } catch (final com.irurueta.numerical.NotReadyException e) {
1026 throw new NotReadyException(e);
1027 } finally {
1028 locked = false;
1029 }
1030 }
1031
1032 /**
1033 * Returns method being used for robust estimation.
1034 *
1035 * @return method being used for robust estimation.
1036 */
1037 @Override
1038 public RobustEstimatorMethod getMethod() {
1039 return RobustEstimatorMethod.PROMEDS;
1040 }
1041
1042 /**
1043 * Sets quality scores corresponding to each provided sample.
1044 * This method is used internally and does not check whether instance is
1045 * locked or not.
1046 *
1047 * @param qualityScores quality scores to be set.
1048 * @throws IllegalArgumentException if provided quality scores length
1049 * is smaller than 3 samples.
1050 */
1051 private void internalSetQualityScores(final double[] qualityScores) {
1052 if (qualityScores == null || qualityScores.length < getMinReadings()) {
1053 throw new IllegalArgumentException();
1054 }
1055
1056 this.qualityScores = qualityScores;
1057 }
1058 }