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