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 }