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