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.AlgebraException;
19 import com.irurueta.algebra.Matrix;
20 import com.irurueta.geometry.Point;
21 import com.irurueta.navigation.LockedException;
22 import com.irurueta.navigation.NotReadyException;
23 import com.irurueta.navigation.indoor.RadioSource;
24 import com.irurueta.navigation.indoor.RangingAndRssiReadingLocated;
25 import com.irurueta.navigation.indoor.RangingReadingLocated;
26 import com.irurueta.navigation.indoor.ReadingLocated;
27 import com.irurueta.navigation.indoor.RssiReadingLocated;
28 import com.irurueta.navigation.indoor.Utils;
29
30 import java.util.ArrayList;
31 import java.util.List;
32
33 /**
34 * This is an abstract class to robustly estimate position, transmitted power and
35 * path loss exponent of a radio source (e.g. Wi-Fi access point or bluetooth
36 * beacon) assuming that the ranging data is available to obtain position with greater
37 * accuracy and that the radio source emits isotropically following the
38 * expression below:
39 * Pr = Pt*Gt*Gr*lambda^2 / (4*pi*d)^2,
40 * where Pr is the received power (expressed in mW),
41 * Gt is the Gain of the transmission antenna
42 * Gr is the Gain of the receiver antenna
43 * d is the distance between emitter and receiver
44 * and lambda is the wavelength and is equal to: lambda = c / f,
45 * where c is the speed of light
46 * and f is the carrier frequency of the radio signal.
47 * Because usually information about the antenna of the radio source cannot be
48 * retrieved (because many measurements are made on unknown devices where
49 * physical access is not possible), this implementation will estimate the
50 * equivalent transmitted power as: Pte = Pt * Gt * Gr.
51 * If Readings contain RSSI standard deviations, those values will be used,
52 * otherwise it will be assumed an RSSI standard deviation of 1 dB.
53 * <p>
54 * This implementation is like RangingAndRssiRadioSourceEstimator but allows mixing
55 * different kinds of located radio source readings (ranging, RSSI and ranging+RSSI).
56 *
57 * @param <S> a {@link RadioSource} type.
58 * @param <P> a {@link Point} type.
59 */
60 public abstract class MixedRadioSourceEstimator<S extends RadioSource, P extends Point<P>>
61 extends RadioSourceEstimator<P, ReadingLocated<P>, MixedRadioSourceEstimatorListener<S, P>> {
62
63 /**
64 * Speed of light expressed in meters per second (m/s).
65 */
66 public static final double SPEED_OF_LIGHT = 299792458.0;
67
68 /**
69 * Default exponent typically used on free space for path loss propagation in
70 * terms of distance. This value is used for free space environments.
71 */
72 public static final double DEFAULT_PATH_LOSS_EXPONENT = 2.0;
73
74 /**
75 * Indicates whether radio source transmitted power estimation is enabled or not by
76 * default. Typically, this data is required for Wi-Fi Access points, but it is already
77 * provided for Beacons (and hence its estimation is not needed).
78 */
79 public static final boolean DEFAULT_TRANSMITTED_POWER_ESTIMATION_ENABLED = true;
80
81 /**
82 * Indicates whether path loss estimation is enabled or not by default.
83 */
84 public static final boolean DEFAULT_PATHLOSS_ESTIMATION_ENABLED = false;
85
86 /**
87 * Indicates that by default position covariances of readings must be taken into account to increase
88 * the amount of standard deviation of each ranging measure by the amount of position standard deviation
89 * assuming that both measures are statistically independent.
90 */
91 public static final boolean DEFAULT_USE_READING_POSITION_COVARIANCES = true;
92
93 /**
94 * RSSI radio source estimator.
95 */
96 protected RssiRadioSourceEstimator<S, P> rssiInnerEstimator;
97
98 /**
99 * Ranging radio source estimator.
100 */
101 protected RangingRadioSourceEstimator<S, P> rangingInnerEstimator;
102
103 /**
104 * Indicates whether transmitted power estimation is enabled or not.
105 */
106 protected boolean transmittedPowerEstimationEnabled = DEFAULT_TRANSMITTED_POWER_ESTIMATION_ENABLED;
107
108 /**
109 * Indicates whether path loss estimation is enabled or not.
110 */
111 protected boolean pathLossEstimationEnabled = DEFAULT_PATHLOSS_ESTIMATION_ENABLED;
112
113 /**
114 * Estimated transmitted power expressed in dBm's or null if not available.
115 */
116 private Double estimatedTransmittedPowerdBm;
117
118 /**
119 * Estimated exponent typically used on free space for path loss propagation in
120 * terms of distance.
121 * On different environments path loss exponent might have different values:
122 * - Free space: 2.0
123 * - Urban Area: 2.7 to 3.5
124 * - Suburban Area: 3 to 5
125 * - Indoor (line-of-sight): 1.6 to 1.8
126 * If path loss exponent estimation is not enabled, this value will always be equal to
127 * {@link #DEFAULT_PATH_LOSS_EXPONENT}
128 */
129 private double estimatedPathLossExponent = DEFAULT_PATH_LOSS_EXPONENT;
130
131 /**
132 * Variance of estimated transmitted power.
133 * This value will only be available when transmitted power
134 * estimation is enabled.
135 */
136 private Double estimatedTransmittedPowerVariance;
137
138 /**
139 * Variance of estimated path loss exponent.
140 * This value will only be available when path-loss
141 * exponent estimation is enabled.
142 */
143 private Double estimatedPathLossExponentVariance;
144
145 /**
146 * Initial transmitted power to start the estimation of radio source
147 * transmitted power.
148 * If not defined, average value of received power readings will be used.
149 */
150 private Double initialTransmittedPowerdBm;
151
152 /**
153 * Initial position to start the estimation of radio source position.
154 * If not defined, centroid of provided readings will be used.
155 */
156 private P initialPosition;
157
158 /**
159 * Initial exponent typically used on free space for path loss propagation in
160 * terms of distance.
161 * On different environments path loss exponent might have different values:
162 * - Free space: 2.0
163 * - Urban Area: 2.7 to 3.5
164 * - Suburban Area: 3 to 5
165 * - Indoor (line-of-sight): 1.6 to 1.8
166 * <p>
167 * If path loss exponent estimation is enabled, estimation will start at this
168 * value and will converge to the most appropriate value.
169 * If path loss exponent estimation is disabled, this value will be assumed
170 * to be exact and the estimated path loss exponent will be equal to this
171 * value.
172 */
173 private double initialPathLossExponent = DEFAULT_PATH_LOSS_EXPONENT;
174
175 /**
176 * Indicates whether position covariances of readings must be taken into account to increase
177 * the amount of standard deviation of each ranging measure by the amount of position standard deviation
178 * assuming that both measures are statistically independent.
179 */
180 private boolean useReadingPositionCovariances = DEFAULT_USE_READING_POSITION_COVARIANCES;
181
182 /**
183 * Number of ranging readings available among all readings.
184 */
185 private int numRangingReadings;
186
187 /**
188 * Number of RSSI readings available among all readings.
189 */
190 private int numRssiReadings;
191
192 /**
193 * Indicates whether position is estimated using RSSI data.
194 * If enough ranging readings are available, this is false and position is estimated using ranging readings,
195 * otherwise this is true and position is estimated using RSSI data in a less reliable way.
196 */
197 private boolean rssiPositionEnabled;
198
199 /**
200 * Indicates whether an homogeneous linear solver is used to estimate an initial
201 * position for the internal ranging radio source estimator.
202 */
203 private boolean useHomogeneousRangingLinearSolver =
204 RangingRadioSourceEstimator.DEFAULT_USE_HOMOGENEOUS_LINEAR_SOLVER;
205
206 /**
207 * Constructor.
208 */
209 protected MixedRadioSourceEstimator() {
210 super();
211 }
212
213 /**
214 * Constructor.
215 * Sets radio signal readings belonging to the same radio source.
216 *
217 * @param readings radio signal readings belonging to the same
218 * radio sources.
219 * @throws IllegalArgumentException if readings are not valid.
220 */
221 protected MixedRadioSourceEstimator(final List<? extends ReadingLocated<P>> readings) {
222 super(readings);
223 }
224
225 /**
226 * Constructor.
227 *
228 * @param listener listener in charge of attending events raised by this instance.
229 */
230 protected MixedRadioSourceEstimator(final MixedRadioSourceEstimatorListener<S, P> listener) {
231 super(listener);
232 }
233
234 /**
235 * Constructor.
236 * Sets radio signal readings belonging to the same radio source.
237 *
238 * @param readings radio signal readings belonging to the same radio source.
239 * @param listener listener in charge of attending events raised by this instance.
240 * @throws IllegalArgumentException if readings are not valid.
241 */
242 protected MixedRadioSourceEstimator(
243 final List<? extends ReadingLocated<P>> readings, final MixedRadioSourceEstimatorListener<S, P> listener) {
244 super(readings, listener);
245 }
246
247 /**
248 * Constructor.
249 *
250 * @param initialPosition initial position to start the estimation of radio
251 * source position.
252 */
253 protected MixedRadioSourceEstimator(final P initialPosition) {
254 this.initialPosition = initialPosition;
255 }
256
257 /**
258 * Constructor.
259 * Sets radio signal readings belonging to the same radio source.
260 *
261 * @param readings radio signal readings belonging to the same radio source.
262 * @param initialPosition initial position to start the estimation of radio
263 * source position.
264 * @throws IllegalArgumentException if readings are not valid.
265 */
266 protected MixedRadioSourceEstimator(
267 final List<? extends ReadingLocated<P>> readings, final P initialPosition) {
268 super(readings);
269 this.initialPosition = initialPosition;
270 }
271
272 /**
273 * Constructor.
274 *
275 * @param initialPosition initial position to start the estimation of radio
276 * source position.
277 * @param listener listener in charge of attending events raised by this instance.
278 */
279 protected MixedRadioSourceEstimator(
280 final P initialPosition, final MixedRadioSourceEstimatorListener<S, P> listener) {
281 super(listener);
282 this.initialPosition = initialPosition;
283 }
284
285 /**
286 * Constructor.
287 * Sets radio signal readings belonging to the same radio source.
288 *
289 * @param readings radio signal readings belonging to the same radio source.
290 * @param initialPosition initial position to start the estimation of radio
291 * source position.
292 * @param listener listener in charge of attending events raised by this instance.
293 * @throws IllegalArgumentException if readings are not valid.
294 */
295 protected MixedRadioSourceEstimator(
296 final List<? extends ReadingLocated<P>> readings, final P initialPosition,
297 final MixedRadioSourceEstimatorListener<S, P> listener) {
298 super(readings, listener);
299 this.initialPosition = initialPosition;
300 }
301
302 /**
303 * Constructor.
304 *
305 * @param initialTransmittedPowerDbm initial transmitted power to start the
306 * estimation of radio source transmitted power
307 * (expressed in dBm's).
308 */
309 protected MixedRadioSourceEstimator(final Double initialTransmittedPowerDbm) {
310 initialTransmittedPowerdBm = initialTransmittedPowerDbm;
311 }
312
313 /**
314 * Constructor.
315 * Sets radio signal readings belonging to the same radio source.
316 *
317 * @param readings radio signal readings belonging to the same radio source.
318 * @param initialTransmittedPowerdBm initial transmitted power to start the
319 * estimation of radio source transmitted power
320 * (expressed in dBm's).
321 * @throws IllegalArgumentException if readings are not valid.
322 */
323 protected MixedRadioSourceEstimator(
324 final List<? extends ReadingLocated<P>> readings, final Double initialTransmittedPowerdBm) {
325 super(readings);
326 this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
327 }
328
329 /**
330 * Constructor.
331 *
332 * @param initialTransmittedPowerdBm initial transmitted power to start the
333 * estimation of radio source transmitted power
334 * (expressed in dBm's).
335 * @param listener listener in charge of attending events raised by this instance.
336 */
337 protected MixedRadioSourceEstimator(
338 final Double initialTransmittedPowerdBm, final MixedRadioSourceEstimatorListener<S, P> listener) {
339 super(listener);
340 this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
341 }
342
343 /**
344 * Constructor.
345 * Sets radio signal readings belonging to the same radio source.
346 *
347 * @param readings radio signal readings belonging to the same radio source.
348 * @param initialTransmittedPowerdBm initial transmitted power to start the
349 * estimation of radio source transmitted power
350 * (expressed in dBm's).
351 * @param listener listener in charge of attending events raised by this instance.
352 * @throws IllegalArgumentException if readings are not valid.
353 */
354 protected MixedRadioSourceEstimator(
355 final List<? extends ReadingLocated<P>> readings, final Double initialTransmittedPowerdBm,
356 final MixedRadioSourceEstimatorListener<S, P> listener) {
357 super(readings, listener);
358 this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
359 }
360
361 /**
362 * Constructor.
363 * Sets radio signal readings belonging to the same radio source.
364 *
365 * @param readings radio signal readings belonging to the same radio source.
366 * @param initialPosition initial position to start the estimation of radio
367 * source position.
368 * @param initialTransmittedPowerdBm initial transmitted power to start the
369 * estimation of radio source transmitted power
370 * (expressed in dBm's).
371 * @throws IllegalArgumentException if readings are not valid.
372 */
373 protected MixedRadioSourceEstimator(
374 final List<? extends ReadingLocated<P>> readings, final P initialPosition,
375 Double initialTransmittedPowerdBm) {
376 super(readings);
377 this.initialPosition = initialPosition;
378 this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
379 }
380
381 /**
382 * Constructor.
383 *
384 * @param initialPosition initial position to start the estimation of radio
385 * source position.
386 * @param initialTransmittedPowerdBm initial transmitted power to start the
387 * estimation of radio source transmitted power
388 * (expressed in dBm's).
389 */
390 protected MixedRadioSourceEstimator(final P initialPosition, final Double initialTransmittedPowerdBm) {
391 this.initialPosition = initialPosition;
392 this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
393 }
394
395 /**
396 * Constructor.
397 *
398 * @param initialPosition initial position to start the estimation of radio
399 * source position.
400 * @param initialTransmittedPowerdBm initial transmitted power to start the
401 * estimation of radio source transmitted power
402 * (expressed in dBm's).
403 * @param listener listener in charge of attending events raised by this instance.
404 */
405 protected MixedRadioSourceEstimator(
406 final P initialPosition, final Double initialTransmittedPowerdBm,
407 final MixedRadioSourceEstimatorListener<S, P> listener) {
408 super(listener);
409 this.initialPosition = initialPosition;
410 this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
411 }
412
413 /**
414 * Constructor.
415 * Sets radio signal readings belonging to the same radio source.
416 *
417 * @param readings radio signal readings belonging to the same radio source.
418 * @param initialPosition initial position to start the estimation of radio
419 * source position.
420 * @param initialTransmittedPowerdBm initial transmitted power to start the
421 * estimation of radio source transmitted power
422 * (expressed in dBm's).
423 * @param listener listener in charge of attending events raised by this instance.
424 * @throws IllegalArgumentException if readings are not valid.
425 */
426 protected MixedRadioSourceEstimator(
427 final List<? extends ReadingLocated<P>> readings, final P initialPosition,
428 final Double initialTransmittedPowerdBm, final MixedRadioSourceEstimatorListener<S, P> listener) {
429 super(readings, listener);
430 this.initialPosition = initialPosition;
431 this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
432 }
433
434 /**
435 * Constructor.
436 * Sets radio signal readings belonging to the same radio source.
437 *
438 * @param readings radio signal readings belonging to the same radio source.
439 * @param initialPosition initial position to start the estimation of radio
440 * source position.
441 * @param initialTransmittedPowerdBm initial transmitted power to start the
442 * estimation of radio source transmitted power
443 * (expressed in dBm's).
444 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
445 * @throws IllegalArgumentException if readings are not valid.
446 */
447 protected MixedRadioSourceEstimator(
448 final List<? extends ReadingLocated<P>> readings, final P initialPosition,
449 final Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
450 this(readings, initialPosition, initialTransmittedPowerdBm);
451 this.initialPathLossExponent = initialPathLossExponent;
452 }
453
454 /**
455 * Constructor.
456 *
457 * @param initialPosition initial position to start the estimation of radio
458 * source position.
459 * @param initialTransmittedPowerdBm initial transmitted power to start the
460 * estimation of radio source transmitted power
461 * (expressed in dBm's).
462 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
463 */
464 protected MixedRadioSourceEstimator(
465 final P initialPosition, final Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
466 this(initialPosition, initialTransmittedPowerdBm);
467 this.initialPathLossExponent = initialPathLossExponent;
468 }
469
470 /**
471 * Constructor.
472 *
473 * @param initialPosition initial position to start the estimation of radio
474 * source position.
475 * @param initialTransmittedPowerdBm initial transmitted power to start the
476 * estimation of radio source transmitted power
477 * (expressed in dBm's).
478 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
479 * @param listener listener in charge of attending events raised by this instance.
480 */
481 protected MixedRadioSourceEstimator(
482 final P initialPosition, final Double initialTransmittedPowerdBm, final double initialPathLossExponent,
483 final MixedRadioSourceEstimatorListener<S, P> listener) {
484 this(initialPosition, initialTransmittedPowerdBm, listener);
485 this.initialPathLossExponent = initialPathLossExponent;
486 }
487
488 /**
489 * Constructor.
490 * Sets radio signal readings belonging to the same radio source.
491 *
492 * @param readings radio signal readings belonging to the same radio source.
493 * @param initialPosition initial position to start the estimation of radio
494 * source position.
495 * @param initialTransmittedPowerdBm initial transmitted power to start the
496 * estimation of radio source transmitted power
497 * (expressed in dBm's).
498 * @param initialPathLossExponent initial path loss exponent. A typical value is 2.0.
499 * @param listener listener in charge of attending events raised by this instance.
500 * @throws IllegalArgumentException if readings are not valid.
501 */
502 protected MixedRadioSourceEstimator(
503 final List<? extends ReadingLocated<P>> readings,
504 final P initialPosition, final Double initialTransmittedPowerdBm, final double initialPathLossExponent,
505 final MixedRadioSourceEstimatorListener<S, P> listener) {
506 this(readings, initialPosition, initialTransmittedPowerdBm, listener);
507 this.initialPathLossExponent = initialPathLossExponent;
508 }
509
510 /**
511 * Gets initial transmitted power to start the estimation of radio source
512 * transmitted power (expressed in dBm's).
513 * If not defined, average value of received power readings will be used.
514 * <p>
515 * If transmitted power estimation is enabled, estimation will start at this
516 * value and will be converted to the most appropriate value.
517 * If transmitted power estimation is disabled, this value will be assumed to be
518 * exact and the estimated transmitted power will be equal to this value
519 * (converted to dBm's).
520 *
521 * @return initial transmitted power to start the estimation of radio source
522 * transmitted power.
523 */
524 public Double getInitialTransmittedPowerdBm() {
525 return initialTransmittedPowerdBm;
526 }
527
528 /**
529 * Sets initial transmitted power to start the estimation of radio source
530 * transmitted power (expressed in dBm's).
531 * If not defined, average value of received power readings will be used.
532 * <p>
533 * If transmitted power estimation is enabled, estimation will start at this
534 * value and will be converted to the most appropriate value.
535 * If transmitted power estimation is disabled, this value will be assumed to be
536 * exact and the estimated transmitted power will be equal to this value
537 * (converted to dBm's).
538 *
539 * @param initialTransmittedPowerdBm initial transmitted power to start the
540 * estimation of radio source transmitted
541 * power.
542 * @throws LockedException if estimator is locked.
543 */
544 public void setInitialTransmittedPowerdBm(final Double initialTransmittedPowerdBm) throws LockedException {
545 if (isLocked()) {
546 throw new LockedException();
547 }
548 this.initialTransmittedPowerdBm = initialTransmittedPowerdBm;
549 }
550
551 /**
552 * Gets initial transmitted power to start the estimation of radio source
553 * transmitted power (expressed in mW).
554 * If not defined, average value of received power readings will be used.
555 * <p>
556 * If transmitted power estimation is enabled, estimation will start at this
557 * value and will be converted to the most appropriate value.
558 * If transmitted power estimation is disabled, this value will be assumed to be
559 * exact and the estimated transmitted power will be equal to this value
560 * (converted to dBm's).
561 *
562 * @return initial transmitted power to start the estimation of radio source
563 * transmitted power.
564 */
565 public Double getInitialTransmittedPower() {
566 return initialTransmittedPowerdBm != null ? Utils.dBmToPower(initialTransmittedPowerdBm) : null;
567 }
568
569 /**
570 * Sets initial transmitted power to start the estimation of radio source
571 * transmitted power (expressed in mW).
572 * If not defined, average value of received power readings will be used.
573 * <p>
574 * If transmitted power estimation is enabled, estimation will start at this
575 * value and will be converted to the most appropriate value.
576 * If transmitted power estimation is disabled, this value will be assumed to be
577 * exact and the estimated transmitted power will be equal to this value
578 * (converted to dBm's).
579 *
580 * @param initialTransmittedPower initial transmitted power to start the
581 * estimation of radio source transmitted power.
582 * @throws LockedException if estimator is locked.
583 * @throws IllegalArgumentException if provided value is negative.
584 */
585 public void setInitialTransmittedPower(final Double initialTransmittedPower) throws LockedException {
586 if (isLocked()) {
587 throw new LockedException();
588 }
589 if (initialTransmittedPower != null) {
590 if (initialTransmittedPower < 0.0) {
591 throw new IllegalArgumentException();
592 }
593 initialTransmittedPowerdBm = Utils.powerTodBm(initialTransmittedPower);
594 } else {
595 initialTransmittedPowerdBm = null;
596 }
597 }
598
599 /**
600 * Indicates whether transmitted power estimation is enabled or not.
601 *
602 * @return true if transmitted power estimation is enabled, false otherwise.
603 */
604 public boolean isTransmittedPowerEstimationEnabled() {
605 return transmittedPowerEstimationEnabled;
606 }
607
608 /**
609 * Specifies whether transmitted power estimation is enabled or not.
610 *
611 * @param transmittedPowerEstimationEnabled true if transmitted power estimation is enabled,
612 * false otherwise.
613 * @throws LockedException if estimator is locked.
614 */
615 public void setTransmittedPowerEstimationEnabled(final boolean transmittedPowerEstimationEnabled)
616 throws LockedException {
617 if (isLocked()) {
618 throw new LockedException();
619 }
620 this.transmittedPowerEstimationEnabled = transmittedPowerEstimationEnabled;
621 }
622
623 /**
624 * Gets initial position to start the estimation of radio source position.
625 * If not defined, centroid of provided readings will be used.
626 * <p>
627 * If position estimation is enabled, estimation will start at this value
628 * and will converge to the most appropriate value.
629 * If position estimation is disabled, this value will be assumed to
630 * be exact and the estimated position will be equal to this value.
631 *
632 * @return initial position to start the estimation of radio source position.
633 */
634 public P getInitialPosition() {
635 return initialPosition;
636 }
637
638 /**
639 * Sets initial position to start the estimation of radio source position.
640 * If not defined, centroid of provided fingerprints will be used.
641 * <p>
642 * If position estimation is enabled, estimation will start at this value
643 * and will converge to the most appropriate value.
644 * If position estimation is disabled, this value will be assumed to
645 * be exact and the estimated position will be equal to this value.
646 *
647 * @param initialPosition initial position to start the estimation of radio
648 * source position.
649 * @throws LockedException if estimator is locked.
650 */
651 public void setInitialPosition(final P initialPosition) throws LockedException {
652 if (isLocked()) {
653 throw new LockedException();
654 }
655 this.initialPosition = initialPosition;
656 }
657
658 /**
659 * Gets initial exponent typically used on free space for path loss propagation
660 * in terms of distance.
661 * On different environments path loss exponent might have different value:
662 * - Free space: 2.0
663 * - Urban Area: 2.7 to 3.5
664 * - Suburban Area: 3 to 5
665 * - Indoor (line-of-sight): 1.6 to 1.8
666 * <p>
667 * If path loss exponent estimation is enabled, estimation will start at this
668 * value and will converge to the most appropriate value.
669 * If path loss exponent estimation is disabled, this value will be assumed
670 * to be exact and the estimated path loss exponent will be equal to this
671 * value.
672 *
673 * @return initial path loss exponent.
674 */
675 public double getInitialPathLossExponent() {
676 return initialPathLossExponent;
677 }
678
679 /**
680 * Sets initial exponent typically used on free space for path loss propagation
681 * in terms of distance.
682 * On different environments path loss exponent might have different value:
683 * - Free space: 2.0
684 * - Urban Area: 2.7 to 3.5
685 * - Suburban Area: 3 to 5
686 * - Indoor (line-of-sight): 1.6 to 1.8
687 * <p>
688 * If path loss exponent estimation is enabled, estimation will start at this
689 * value and will converge to the most appropriate value.
690 * If path loss exponent estimation is disabled, this value will be assumed
691 * to be exact and the estimated path loss exponent will be equal to this
692 * value.
693 *
694 * @param initialPathLossExponent initial path loss exponent.
695 * @throws LockedException if estimator is locked.
696 */
697 public void setInitialPathLossExponent(final double initialPathLossExponent) throws LockedException {
698 if (isLocked()) {
699 throw new LockedException();
700 }
701 this.initialPathLossExponent = initialPathLossExponent;
702 }
703
704 /**
705 * Indicates whether path loss estimation is enabled or not.
706 *
707 * @return true if path loss estimation is enabled, false otherwise.
708 */
709 public boolean isPathLossEstimationEnabled() {
710 return pathLossEstimationEnabled;
711 }
712
713 /**
714 * Specifies whether path loss estimation is enabled or not.
715 *
716 * @param pathLossEstimationEnabled true if path loss estimation is enabled,
717 * false otherwise.
718 * @throws LockedException if estimator is locked.
719 */
720 public void setPathLossEstimationEnabled(final boolean pathLossEstimationEnabled) throws LockedException {
721 if (isLocked()) {
722 throw new LockedException();
723 }
724 this.pathLossEstimationEnabled = pathLossEstimationEnabled;
725 }
726
727 /**
728 * Indicates whether position covariances of readings must be taken into account to increase
729 * the amount of standard deviation of each ranging measure by the amount of position standard
730 * deviation assuming that both measures are statistically independent.
731 *
732 * @return true to take into account reading position covariances, false otherwise.
733 */
734 public boolean getUseReadingPositionCovariance() {
735 return useReadingPositionCovariances;
736 }
737
738 /**
739 * Specifies whether position covariances of readings must be taken into account to increase
740 * the amount of standard deviation of each ranging measure by the amount of position standard
741 * deviation assuming that both measures are statistically independent.
742 *
743 * @param useReadingPositionCovariances true to take into account reading position covariances, false
744 * otherwise.
745 * @throws LockedException if estimator is locked.
746 */
747 public void setUseReadingPositionCovariances(final boolean useReadingPositionCovariances) throws LockedException {
748 if (isLocked()) {
749 throw new LockedException();
750 }
751 this.useReadingPositionCovariances = useReadingPositionCovariances;
752 }
753
754 /**
755 * Indicates whether an homogeneous linear solver is used to estimate an initial
756 * position for the internal ranging radio source estimator.
757 *
758 * @return true if homogeneous linear solver is used, false if an inhomogeneous linear
759 * one is used instead.
760 */
761 public boolean isHomogeneousRangingLinearSolverUsed() {
762 return useHomogeneousRangingLinearSolver;
763 }
764
765 /**
766 * Specifies whether an homogeneous linear solver is used to estimate an initial
767 * position for the internal ranging radio source estimator.
768 *
769 * @param useHomogeneousLinearSolver true if homogeneous linear solver is used, false
770 * if an inhomogeneous linear one is used instead.
771 * @throws LockedException if estimator is locked.
772 */
773 public void setHomogeneousRangingLinearSolverUsed(final boolean useHomogeneousLinearSolver) throws LockedException {
774 if (isLocked()) {
775 throw new LockedException();
776 }
777
778 useHomogeneousRangingLinearSolver = useHomogeneousLinearSolver;
779 }
780
781 /**
782 * Gets minimum required number of ranging or ranging+rssi readings
783 * required to start estimation.
784 *
785 * @return minimum required number of ranging or ranging+rssi readings.
786 */
787 public int getMinRangingReadings() {
788 return getNumberOfDimensions() + 1;
789 }
790
791 /**
792 * Gets minimum required number of rssi or ranging+rssi readings
793 * required to start estimation.
794 *
795 * @return minimum required number of rssi or ranging+rssi readings.
796 */
797 public int getMinRssiReadings() {
798 return getMinReadings();
799 }
800
801 /**
802 * Gets minimum required number of readings to estimate
803 * power, position and path-loss exponent.
804 * This value depends on the number of parameters to
805 * be estimated, but for position only, this is 3
806 * readings.
807 *
808 * @return minimum required number of readings.
809 * @throws IllegalStateException if inner RSSI estimator is busy.
810 */
811 @Override
812 public int getMinReadings() {
813 createInnerEstimatorsIfNeeded();
814
815 var result = getNumberOfDimensions();
816 if (rssiInnerEstimator != null && (transmittedPowerEstimationEnabled || pathLossEstimationEnabled)) {
817 try {
818 rssiInnerEstimator.setPositionEstimationEnabled(rssiPositionEnabled);
819 rssiInnerEstimator.setTransmittedPowerEstimationEnabled(transmittedPowerEstimationEnabled);
820 rssiInnerEstimator.setPathLossEstimationEnabled(pathLossEstimationEnabled);
821 } catch (final LockedException e) {
822 throw new IllegalStateException(e);
823 }
824
825 result += rssiInnerEstimator.getMinReadings();
826 } else {
827 result++;
828 }
829
830 return result;
831 }
832
833 /**
834 * Gets estimated radio source position.
835 *
836 * @return estimated radio source position.
837 */
838 public P getEstimatedPosition() {
839 return rssiPositionEnabled ? rssiInnerEstimator.getEstimatedPosition()
840 : rangingInnerEstimator.getEstimatedPosition();
841 }
842
843 /**
844 * Indicates whether readings are valid or not.
845 * Readings are considered valid when there are enough readings.
846 *
847 * @param readings readings to be validated.
848 * @return true if readings are valid, false otherwise.
849 */
850 @Override
851 public boolean areValidReadings(final List<? extends ReadingLocated<P>> readings) {
852 if (readings == null) {
853 return false;
854 }
855
856 checkReadings(readings);
857
858 // if enough ranging data is available, we check validity both for ranging and RSSI readings
859 return ((!rssiPositionEnabled && numRangingReadings >= getMinRangingReadings()
860 && numRssiReadings >= getMinRssiReadings())
861 // if not enough ranging data is available, we check validity only for RSSI readings
862 || (rssiPositionEnabled && numRssiReadings >= getMinRssiReadings())
863 // if only position is enabled, then only check for ranging readings
864 || (!transmittedPowerEstimationEnabled && !pathLossEstimationEnabled
865 && numRangingReadings >= getMinRangingReadings()))
866 // in both upper cases enough general readings must be available
867 && super.areValidReadings(readings);
868 }
869
870 /**
871 * Indicates whether this instance is ready to start the estimation.
872 *
873 * @return true if this instance is ready, false otherwise.
874 */
875 @Override
876 public boolean isReady() {
877 return areValidReadings(readings);
878 }
879
880 /**
881 * Estimate position, transmitted power and path loss exponent.
882 *
883 * @throws RadioSourceEstimationException if estimation fails.
884 * @throws NotReadyException if estimator is not ready.
885 * @throws LockedException if estimator is locked.
886 */
887 @SuppressWarnings("DuplicatedCode")
888 @Override
889 public void estimate() throws RadioSourceEstimationException, NotReadyException, LockedException {
890 if (isLocked()) {
891 throw new LockedException();
892 }
893 if (!isReady()) {
894 throw new NotReadyException();
895 }
896
897 try {
898 locked = true;
899
900 if (listener != null) {
901 listener.onEstimateStart(this);
902 }
903
904 createInnerEstimatorsIfNeeded();
905
906 final var rangingReadings = new ArrayList<RangingReadingLocated<S, P>>();
907 final var rssiReadings = new ArrayList<RssiReadingLocated<S, P>>();
908 for (final var reading : readings) {
909 if (reading instanceof RangingReadingLocated) {
910 rangingReadings.add((RangingReadingLocated<S, P>) reading);
911
912 } else if (reading instanceof RssiReadingLocated) {
913 rssiReadings.add((RssiReadingLocated<S, P>) reading);
914
915 } else if (reading instanceof RangingAndRssiReadingLocated) {
916 rangingReadings.add(createRangingReading((RangingAndRssiReadingLocated<S, P>) reading));
917 rssiReadings.add(createRssiReading((RangingAndRssiReadingLocated<S, P>) reading));
918 }
919 }
920
921 // estimate position using ranging data, if possible
922 P estimatedPosition = null;
923 if (!rssiPositionEnabled) {
924 rangingInnerEstimator.setUseReadingPositionCovariances(useReadingPositionCovariances);
925 rangingInnerEstimator.setHomogeneousLinearSolverUsed(useHomogeneousRangingLinearSolver);
926 rangingInnerEstimator.setReadings(rangingReadings);
927 rangingInnerEstimator.setInitialPosition(initialPosition);
928
929 rangingInnerEstimator.estimate();
930
931 estimatedPositionCoordinates = rangingInnerEstimator.getEstimatedPositionCoordinates();
932 estimatedPositionCovariance = rangingInnerEstimator.getEstimatedPositionCovariance();
933 estimatedPosition = rangingInnerEstimator.getEstimatedPosition();
934 }
935
936 // estimate transmitted power and/or path-loss if enabled
937 if (transmittedPowerEstimationEnabled || pathLossEstimationEnabled || rssiPositionEnabled) {
938 rssiInnerEstimator.setPositionEstimationEnabled(rssiPositionEnabled);
939 rssiInnerEstimator.setInitialPosition(estimatedPosition);
940
941 rssiInnerEstimator.setTransmittedPowerEstimationEnabled(transmittedPowerEstimationEnabled);
942 rssiInnerEstimator.setInitialTransmittedPowerdBm(initialTransmittedPowerdBm);
943
944 rssiInnerEstimator.setPathLossEstimationEnabled(pathLossEstimationEnabled);
945 rssiInnerEstimator.setInitialPathLossExponent(initialPathLossExponent);
946
947 rssiInnerEstimator.setReadings(rssiReadings);
948
949 rssiInnerEstimator.estimate();
950
951 if (rssiPositionEnabled) {
952 estimatedPositionCoordinates = rssiInnerEstimator.getEstimatedPositionCoordinates();
953 estimatedPositionCovariance = rssiInnerEstimator.getEstimatedPositionCovariance();
954 }
955
956 if (transmittedPowerEstimationEnabled) {
957 // transmitted power estimation enabled
958 estimatedTransmittedPowerdBm = rssiInnerEstimator.getEstimatedTransmittedPowerdBm();
959 estimatedTransmittedPowerVariance = rssiInnerEstimator.getEstimatedTransmittedPowerVariance();
960 } else {
961 // transmitted power estimation disabled
962 estimatedTransmittedPowerdBm = initialTransmittedPowerdBm;
963 estimatedTransmittedPowerVariance = null;
964 }
965
966 if (pathLossEstimationEnabled) {
967 // path-loss exponent estimation enabled
968 estimatedPathLossExponent = rssiInnerEstimator.getEstimatedPathLossExponent();
969 estimatedPathLossExponentVariance = rssiInnerEstimator.getEstimatedPathLossExponentVariance();
970 } else {
971 // path-loss exponent estimation disabled
972 estimatedPathLossExponent = initialPathLossExponent;
973 estimatedPathLossExponentVariance = null;
974 }
975
976 // build covariance matrix
977 if (rssiPositionEnabled) {
978 // if only RSSI estimation is done, we use directly the available estimated covariance
979 estimatedCovariance = rssiInnerEstimator.getEstimatedCovariance();
980
981 } else {
982 // if both ranging and RSSI data is used, we build covariance matrix by setting
983 // position covariance estimated by ranging estimator into top-left corner, and then
984 // adding covariance terms related to path-loss exponent and transmitted power
985 final var rssiCov = rssiInnerEstimator.getEstimatedCovariance();
986 if (estimatedPositionCovariance != null && rssiCov != null) {
987 final var dims = getNumberOfDimensions();
988 var n = dims;
989 if (transmittedPowerEstimationEnabled) {
990 n++;
991 }
992 if (pathLossEstimationEnabled) {
993 n++;
994 }
995
996 final var dimsMinus1 = dims - 1;
997 final var nMinus1 = n - 1;
998 estimatedCovariance = new Matrix(n, n);
999 estimatedCovariance.setSubmatrix(0, 0, dimsMinus1, dimsMinus1,
1000 estimatedPositionCovariance);
1001 estimatedCovariance.setSubmatrix(dims, dims, nMinus1, nMinus1, rssiCov);
1002 } else {
1003 estimatedCovariance = null;
1004 }
1005 }
1006
1007 } else {
1008 estimatedCovariance = estimatedPositionCovariance;
1009 estimatedTransmittedPowerdBm = initialTransmittedPowerdBm;
1010 estimatedTransmittedPowerVariance = null;
1011
1012 estimatedPathLossExponent = initialPathLossExponent;
1013 estimatedPathLossExponentVariance = null;
1014 }
1015
1016 if (listener != null) {
1017 listener.onEstimateEnd(this);
1018 }
1019
1020 } catch (final AlgebraException e) {
1021 throw new RadioSourceEstimationException(e);
1022 } finally {
1023 locked = false;
1024 }
1025 }
1026
1027 /**
1028 * Gets estimated transmitted power expressed in milli watts (mW) or null if
1029 * not available.
1030 *
1031 * @return estimated transmitted power expressed in milli watts or null.
1032 */
1033 public Double getEstimatedTransmittedPower() {
1034 return estimatedTransmittedPowerdBm != null ? Utils.dBmToPower(estimatedTransmittedPowerdBm) : null;
1035 }
1036
1037 /**
1038 * Gets estimated transmitted power expressed in dBm's or null if not available.
1039 *
1040 * @return estimated transmitted power expressed in dBm's or null.
1041 */
1042 public Double getEstimatedTransmittedPowerdBm() {
1043 return estimatedTransmittedPowerdBm;
1044 }
1045
1046 /**
1047 * Gets estimated exponent typically used on free space for path loss propagation in
1048 * terms of distance.
1049 * On different environments path loss exponent might have different values:
1050 * - Free space: 2.0
1051 * - Urban Area: 2.7 to 3.5
1052 * - Suburban Area: 3 to 5
1053 * - Indoor (line-of-sight): 1.6 to 1.8
1054 * If path loss exponent estimation is not enabled, this value will always be equal to
1055 * {@link #DEFAULT_PATH_LOSS_EXPONENT}
1056 *
1057 * @return estimated path loss exponent.
1058 */
1059 public double getEstimatedPathLossExponent() {
1060 return estimatedPathLossExponent;
1061 }
1062
1063 /**
1064 * Gets estimated transmitted power variance.
1065 * This value will only be available when transmitted power
1066 * estimation is enabled.
1067 *
1068 * @return estimated transmitted power variance or null.
1069 */
1070 public Double getEstimatedTransmittedPowerVariance() {
1071 return estimatedTransmittedPowerVariance;
1072 }
1073
1074 /**
1075 * Gets estimated path loss exponent variance.
1076 * This value will only be available when path-loss
1077 * exponent estimation is enabled.
1078 *
1079 * @return estimated path loss exponent variance or null.
1080 */
1081 public Double getEstimatedPathLossExponentVariance() {
1082 return estimatedPathLossExponentVariance;
1083 }
1084
1085 /**
1086 * Creates inner estimators if needed.
1087 */
1088 protected abstract void createInnerEstimatorsIfNeeded();
1089
1090 /**
1091 * Creates a ranging reading from a ranging and RSSI reading.
1092 *
1093 * @param reading input reading to convert from.
1094 * @return a ranging reading containing only the ranging data of input reading.
1095 */
1096 private RangingReadingLocated<S, P> createRangingReading(final RangingAndRssiReadingLocated<S, P> reading) {
1097 return new RangingReadingLocated<>(reading.getSource(), reading.getDistance(), reading.getPosition(),
1098 reading.getDistanceStandardDeviation(), reading.getPositionCovariance());
1099 }
1100
1101 /**
1102 * Creates an RSSI reading from a ranging and RSSI reading.
1103 *
1104 * @param reading input reading to convert from.
1105 * @return an RSSI reading containing only the RSSI data of input reading.
1106 */
1107 private RssiReadingLocated<S, P> createRssiReading(final RangingAndRssiReadingLocated<S, P> reading) {
1108 return new RssiReadingLocated<>(reading.getSource(), reading.getRssi(), reading.getPosition(),
1109 reading.getRssiStandardDeviation(), reading.getPositionCovariance());
1110 }
1111
1112 /**
1113 * Checks number of available ranging readings and number of available RSSI readings. Also determines
1114 * whether position must be estimated using ranging data or RSSI data.
1115 *
1116 * @param readings readings to be checked.
1117 */
1118 private void checkReadings(final List<? extends ReadingLocated<P>> readings) {
1119 numRangingReadings = numRssiReadings = 0;
1120
1121 if (readings == null) {
1122 return;
1123 }
1124
1125 for (final var reading : readings) {
1126 if (reading instanceof RangingReadingLocated) {
1127 numRangingReadings++;
1128
1129 } else if (reading instanceof RssiReadingLocated) {
1130 numRssiReadings++;
1131
1132 } else if (reading instanceof RangingAndRssiReadingLocated) {
1133 numRangingReadings++;
1134 numRssiReadings++;
1135 }
1136 }
1137
1138 rssiPositionEnabled = numRangingReadings < getMinRangingReadings();
1139 }
1140 }