View Javadoc
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 }