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.geometry.Point3D;
19  import com.irurueta.navigation.LockedException;
20  import com.irurueta.navigation.NotReadyException;
21  import com.irurueta.navigation.indoor.RadioSource;
22  import com.irurueta.navigation.indoor.RssiReadingLocated;
23  import com.irurueta.numerical.robust.MSACRobustEstimator;
24  import com.irurueta.numerical.robust.MSACRobustEstimatorListener;
25  import com.irurueta.numerical.robust.RobustEstimator;
26  import com.irurueta.numerical.robust.RobustEstimatorException;
27  import com.irurueta.numerical.robust.RobustEstimatorMethod;
28  
29  import java.util.List;
30  
31  /**
32   * Robustly estimate 3D position, transmitted power and path-loss exponent of a radio source
33   * (e.g. Wi-Fi access point or bluetooth beacon), by discarding outliers using MSAC
34   * algorithm and assuming that the radio source emits isotropically following the
35   * expression below:
36   * Pr = Pt*Gt*Gr*lambda^2 / (4*pi*d)^2,
37   * where Pr is the received power (expressed in mW),
38   * Gt is the Gain of the transmission antenna
39   * Gr is the Gain of the receiver antenna
40   * d is the distance between emitter and receiver
41   * and lambda is the wavelength and is equal to: lambda = c / f,
42   * where c is the speed of light
43   * and f is the carrier frequency of the radio signal.
44   * Because usually information about the antenna of the radio source cannot be
45   * retrieved (because many measurements are made on unknown access points where
46   * physical access is not possible), this implementation will estimate the
47   * equivalent transmitted power as: Pte = Pt * Gt * Gr.
48   * If WifiReadings contain RSSI standard deviations, those values will be used,
49   * otherwise it will be assumed an RSSI standard deviation of 1 dB.
50   * Implementations of this class should be able to detect and discard outliers in
51   * order to find the best solution.
52   * <p>
53   * IMPORTANT: When using this class estimation can be done using a
54   * combination of radio source position, transmitted power and path loss
55   * exponent. However enabling all three estimations usually achieves
56   * inaccurate results. When using this class, estimation must be of at least
57   * one parameter (position, transmitted power or path loss exponent) when
58   * initial values are provided for the other two, and at most it should consist
59   * of two parameters (either position and transmitted power, position and
60   * path loss exponent or transmitted power and path loss exponent), providing an
61   * initial value for the remaining parameter.
62   *
63   * @param <S> a {@link RadioSource} type.*
64   */
65  @SuppressWarnings("Duplicates")
66  public class MSACRobustRssiRadioSourceEstimator3D<S extends RadioSource> extends RobustRssiRadioSourceEstimator3D<S> {
67  
68      /**
69       * Constant defining default threshold to determine whether samples are
70       * inliers or not.
71       */
72      public static final double DEFAULT_THRESHOLD = 0.1;
73  
74      /**
75       * Minimum value that can be set as threshold.
76       * Threshold must be strictly greater than 0.0.
77       */
78      public static final double MIN_THRESHOLD = 0.0;
79  
80      /**
81       * Threshold to determine whether samples are inliers or not when
82       * testing possible estimation solutions.
83       */
84      private double threshold = DEFAULT_THRESHOLD;
85  
86      /**
87       * Constructor.
88       */
89      public MSACRobustRssiRadioSourceEstimator3D() {
90          super();
91      }
92  
93      /**
94       * Constructor.
95       * Sets signal readings belonging to the same radio source.
96       *
97       * @param readings signal readings belonging to the same radio source.
98       * @throws IllegalArgumentException if readings are not valid.
99       */
100     public MSACRobustRssiRadioSourceEstimator3D(final List<? extends RssiReadingLocated<S, Point3D>> readings) {
101         super(readings);
102     }
103 
104     /**
105      * Constructor.
106      *
107      * @param listener listener in charge of attending events raised by this instance.
108      */
109     public MSACRobustRssiRadioSourceEstimator3D(final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
110         super(listener);
111     }
112 
113     /**
114      * Constructor.
115      * Sets signal readings belonging to the same radio source.
116      *
117      * @param readings signal readings belonging to the same radio source.
118      * @param listener listener in charge of attending events raised by this instance.
119      * @throws IllegalArgumentException if readings are not valid.
120      */
121     public MSACRobustRssiRadioSourceEstimator3D(
122             final List<? extends RssiReadingLocated<S, Point3D>> readings,
123             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
124         super(readings, listener);
125     }
126 
127     /**
128      * Constructor.
129      * Sets signal readings belonging to the same radio source.
130      *
131      * @param readings        signal readings belonging to the same radio source.
132      * @param initialPosition initial position to start the estimation of radio
133      *                        source position.
134      * @throws IllegalArgumentException if readings are not valid.
135      */
136     public MSACRobustRssiRadioSourceEstimator3D(
137             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition) {
138         super(readings, initialPosition);
139     }
140 
141     /**
142      * Constructor.
143      *
144      * @param initialPosition initial position to start the estimation of radio
145      *                        source position.
146      */
147     public MSACRobustRssiRadioSourceEstimator3D(final Point3D initialPosition) {
148         super(initialPosition);
149     }
150 
151     /**
152      * Constructor.
153      *
154      * @param initialPosition initial position to start the estimation of radio
155      *                        source position.
156      * @param listener        listener in charge of attending events raised by this instance.
157      */
158     public MSACRobustRssiRadioSourceEstimator3D(
159             final Point3D initialPosition, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
160         super(initialPosition, listener);
161     }
162 
163     /**
164      * Constructor.
165      * Sets signal readings belonging to the same radio source.
166      *
167      * @param readings        signal readings belonging to the same radio source.
168      * @param initialPosition initial position to start the estimation of radio
169      *                        source position.
170      * @param listener        listener in charge of attending events raised by this instance.
171      * @throws IllegalArgumentException if readings are not valid.
172      */
173     public MSACRobustRssiRadioSourceEstimator3D(
174             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
175             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
176         super(readings, initialPosition, listener);
177     }
178 
179     /**
180      * Constructor.
181      *
182      * @param initialTransmittedPowerdBm initial transmitted power to start the
183      *                                   estimation of radio source transmitted power
184      *                                   (expressed in dBm's)
185      */
186     public MSACRobustRssiRadioSourceEstimator3D(final Double initialTransmittedPowerdBm) {
187         super(initialTransmittedPowerdBm);
188     }
189 
190     /**
191      * Constructor.
192      * Sets signal readings belonging to the same radio source.
193      *
194      * @param readings                   signal readings belonging to the same radio source.
195      * @param initialTransmittedPowerdBm initial transmitted power to start the
196      *                                   estimation of access point transmitted power
197      *                                   (expressed in dBm's)
198      * @throws IllegalArgumentException if readings are not valid.
199      */
200     public MSACRobustRssiRadioSourceEstimator3D(
201             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Double initialTransmittedPowerdBm) {
202         super(readings, initialTransmittedPowerdBm);
203     }
204 
205     /**
206      * Constructor.
207      *
208      * @param initialTransmittedPowerdBm initial transmitted power to start the
209      *                                   estimation of radio source transmitted power
210      *                                   (expressed in dBm's)
211      * @param listener                   listener in charge of attending events raised by this instance.
212      */
213     public MSACRobustRssiRadioSourceEstimator3D(
214             final Double initialTransmittedPowerdBm,
215             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
216         super(initialTransmittedPowerdBm, listener);
217     }
218 
219     /**
220      * Constructor.
221      * Sets signal readings belonging to the same radio source.
222      *
223      * @param readings                   signal readings belonging to the same radio source.
224      * @param initialTransmittedPowerdBm initial transmitted power to start the
225      *                                   estimation of radio source transmitted power
226      *                                   (expressed in dBm's)
227      * @param listener                   listener in charge of attending events raised by this instance.
228      * @throws IllegalArgumentException if readings are not valid.
229      */
230     public MSACRobustRssiRadioSourceEstimator3D(
231             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Double initialTransmittedPowerdBm,
232             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
233         super(readings, initialTransmittedPowerdBm, listener);
234     }
235 
236     /**
237      * Constructor.
238      * Sets signal readings belonging to the same radio source.
239      *
240      * @param readings                   signal readings belonging to the same radio source.
241      * @param initialPosition            initial position to start the estimation of radio
242      *                                   source position.
243      * @param initialTransmittedPowerdBm initial transmitted power to start the
244      *                                   estimation of radio source transmitted power
245      *                                   (expressed in dBm's).
246      * @throws IllegalArgumentException if readings are not valid.
247      */
248     public MSACRobustRssiRadioSourceEstimator3D(
249             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
250             final Double initialTransmittedPowerdBm) {
251         super(readings, initialPosition, initialTransmittedPowerdBm);
252     }
253 
254     /**
255      * Constructor.
256      *
257      * @param initialPosition            initial position to start the estimation of radio
258      *                                   source position.
259      * @param initialTransmittedPowerdBm initial transmitted power to start the
260      *                                   estimation of radio source transmitted power
261      *                                   (expressed in dBm's).
262      */
263     public MSACRobustRssiRadioSourceEstimator3D(
264             final Point3D initialPosition, final Double initialTransmittedPowerdBm) {
265         super(initialPosition, initialTransmittedPowerdBm);
266     }
267 
268     /**
269      * Constructor.
270      *
271      * @param initialPosition            initial position to start the estimation of radio
272      *                                   source position.
273      * @param initialTransmittedPowerdBm initial transmitted power to start the
274      *                                   estimation of radio source transmitted power
275      *                                   (expressed in dBm's).
276      * @param listener                   in charge of attending events raised by this instance.
277      */
278     public MSACRobustRssiRadioSourceEstimator3D(
279             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
280             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
281         super(initialPosition, initialTransmittedPowerdBm, listener);
282     }
283 
284     /**
285      * Constructor.
286      * Sets signal readings belonging to the same radio source.
287      *
288      * @param readings                   signal readings belonging to the same radio source.
289      * @param initialPosition            initial position to start the estimation of radio
290      *                                   source position.
291      * @param initialTransmittedPowerdBm initial transmitted power to start the
292      *                                   estimation of radio source transmitted power
293      *                                   (expressed in dBm's).
294      * @param listener                   listener in charge of attending events raised by this instance.
295      * @throws IllegalArgumentException if readings are not valid.
296      */
297     public MSACRobustRssiRadioSourceEstimator3D(
298             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
299             final Double initialTransmittedPowerdBm, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
300         super(readings, initialPosition, initialTransmittedPowerdBm, listener);
301     }
302 
303     /**
304      * Constructor.
305      * Sets signal readings belonging to the same radio source.
306      *
307      * @param readings                   signal readings belonging to the same radio source.
308      * @param initialPosition            initial position to start the estimation of radio
309      *                                   source position.
310      * @param initialTransmittedPowerdBm initial transmitted power to start the
311      *                                   estimation of radio source transmitted power
312      *                                   (expressed in dBm's).
313      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
314      * @throws IllegalArgumentException if readings are not valid.
315      */
316     public MSACRobustRssiRadioSourceEstimator3D(
317             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
318             final Double initialTransmittedPowerdBm, final double initialPathLossExponent) {
319         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
320     }
321 
322     /**
323      * Constructor.
324      *
325      * @param initialPosition            initial position to start the estimation of radio
326      *                                   source position.
327      * @param initialTransmittedPowerdBm initial transmitted power to start the
328      *                                   estimation of radio source transmitted power
329      *                                   (expressed in dBm's).
330      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
331      */
332     public MSACRobustRssiRadioSourceEstimator3D(
333             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
334             final double initialPathLossExponent) {
335         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent);
336     }
337 
338     /**
339      * Constructor.
340      *
341      * @param initialPosition            initial position to start the estimation of radio
342      *                                   source position.
343      * @param initialTransmittedPowerdBm initial transmitted power to start the
344      *                                   estimation of radio source transmitted power
345      *                                   (expressed in dBm's).
346      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
347      * @param listener                   listener in charge of attending events raised by this instance.
348      */
349     public MSACRobustRssiRadioSourceEstimator3D(
350             final Point3D initialPosition, final Double initialTransmittedPowerdBm,
351             final double initialPathLossExponent, final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
352         super(initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
353     }
354 
355     /**
356      * Constructor.
357      * Sets signal readings belonging to the same radio source.
358      *
359      * @param readings                   signal readings belonging to the same radio source.
360      * @param initialPosition            initial position to start the estimation of radio
361      *                                   source position.
362      * @param initialTransmittedPowerdBm initial transmitted power to start the
363      *                                   estimation of radio source transmitted power
364      *                                   (expressed in dBm's).
365      * @param initialPathLossExponent    initial path loss exponent. A typical value is 2.0.
366      * @param listener                   listener in charge of attending events raised by this instance.
367      * @throws IllegalArgumentException if readings are not valid.
368      */
369     public MSACRobustRssiRadioSourceEstimator3D(
370             final List<? extends RssiReadingLocated<S, Point3D>> readings, final Point3D initialPosition,
371             final Double initialTransmittedPowerdBm, final double initialPathLossExponent,
372             final RobustRssiRadioSourceEstimatorListener<S, Point3D> listener) {
373         super(readings, initialPosition, initialTransmittedPowerdBm, initialPathLossExponent, listener);
374     }
375 
376     /**
377      * Returns threshold to determine whether samples are inliers or not.
378      *
379      * @return threshold to determine whether samples are inliers or not.
380      */
381     public double getThreshold() {
382         return threshold;
383     }
384 
385     /**
386      * Sets threshold to determine whether samples are inliers or not.
387      *
388      * @param threshold threshold to be set.
389      * @throws IllegalArgumentException if provided value is equal or less than
390      *                                  zero.
391      * @throws LockedException          if robust estimator is locked because an
392      *                                  estimation is already in progress.
393      */
394     public void setThreshold(final double threshold) throws LockedException {
395         if (isLocked()) {
396             throw new LockedException();
397         }
398         if (threshold <= MIN_THRESHOLD) {
399             throw new IllegalArgumentException();
400         }
401         this.threshold = threshold;
402     }
403 
404     /**
405      * Robustly estimates position, transmitted power and path-loss exponent for a
406      * radio source.
407      *
408      * @throws LockedException          if instance is busy during estimation.
409      * @throws NotReadyException        if estimator is not ready.
410      * @throws RobustEstimatorException if estimation fails for any reason
411      *                                  (i.e. numerical instability, no solution available, etc).
412      */
413     @Override
414     public void estimate() throws LockedException, NotReadyException, RobustEstimatorException {
415         if (isLocked()) {
416             throw new LockedException();
417         }
418         if (!isReady()) {
419             throw new NotReadyException();
420         }
421 
422         final var innerEstimator = new MSACRobustEstimator<>(new MSACRobustEstimatorListener<Solution<Point3D>>() {
423             @Override
424             public double getThreshold() {
425                 return threshold;
426             }
427 
428             @Override
429             public int getTotalSamples() {
430                 return readings.size();
431             }
432 
433             @Override
434             public int getSubsetSize() {
435                 return Math.max(preliminarySubsetSize, getMinReadings());
436             }
437 
438             @Override
439             public void estimatePreliminarSolutions(
440                     final int[] samplesIndices, final List<Solution<Point3D>> solutions) {
441                 solvePreliminarySolutions(samplesIndices, solutions);
442             }
443 
444             @Override
445             public double computeResidual(final Solution<Point3D> currentEstimation, final int i) {
446                 return residual(currentEstimation, i);
447             }
448 
449             @Override
450             public boolean isReady() {
451                 return MSACRobustRssiRadioSourceEstimator3D.this.isReady();
452             }
453 
454             @Override
455             public void onEstimateStart(final RobustEstimator<Solution<Point3D>> estimator) {
456                 // no action needed
457             }
458 
459             @Override
460             public void onEstimateEnd(final RobustEstimator<Solution<Point3D>> estimator) {
461                 // no action needed
462             }
463 
464             @Override
465             public void onEstimateNextIteration(
466                     final RobustEstimator<Solution<Point3D>> estimator, final int iteration) {
467                 if (listener != null) {
468                     listener.onEstimateNextIteration(MSACRobustRssiRadioSourceEstimator3D.this, iteration);
469                 }
470             }
471 
472             @Override
473             public void onEstimateProgressChange(
474                     final RobustEstimator<Solution<Point3D>> estimator, final float progress) {
475                 if (listener != null) {
476                     listener.onEstimateProgressChange(MSACRobustRssiRadioSourceEstimator3D.this, progress);
477                 }
478             }
479         });
480 
481         try {
482             locked = true;
483 
484             if (listener != null) {
485                 listener.onEstimateStart(this);
486             }
487 
488             inliersData = null;
489             innerEstimator.setConfidence(confidence);
490             innerEstimator.setMaxIterations(maxIterations);
491             innerEstimator.setProgressDelta(progressDelta);
492             final var result = innerEstimator.estimate();
493             inliersData = innerEstimator.getInliersData();
494             attemptRefine(result);
495 
496             if (listener != null) {
497                 listener.onEstimateEnd(this);
498             }
499 
500         } catch (final com.irurueta.numerical.LockedException e) {
501             throw new LockedException(e);
502         } catch (final com.irurueta.numerical.NotReadyException e) {
503             throw new NotReadyException(e);
504         } finally {
505             locked = false;
506         }
507     }
508 
509     /**
510      * Returns method being used for robust estimation.
511      *
512      * @return method being used for robust estimation.
513      */
514     @Override
515     public RobustEstimatorMethod getMethod() {
516         return RobustEstimatorMethod.MSAC;
517     }
518 }