1 /*
2 * Copyright (C) 2019 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.fingerprint;
17
18 import com.irurueta.geometry.Point;
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.RadioSourceLocated;
23 import com.irurueta.navigation.indoor.RssiFingerprint;
24 import com.irurueta.navigation.indoor.RssiFingerprintLocated;
25 import com.irurueta.navigation.indoor.RssiReading;
26
27 import java.util.List;
28
29 /**
30 * Base class for position and radio source estimators based on located fingerprints
31 * containing only RSSI readings without any prior knowledge of radio sources.
32 *
33 * @param <P> a {@link Point} type.
34 * @param <L> a {@link BaseFingerprintPositionAndRadioSourceEstimatorListener} type.
35 */
36 public abstract class BaseFingerprintPositionAndRadioSourceEstimator<P extends Point<?>,
37 L extends BaseFingerprintPositionAndRadioSourceEstimatorListener<?>> {
38
39 /**
40 * Default minimum number of nearest fingerprints to search.
41 * This is the minimum required value to be used for the estimation of all
42 * radio sources and fingerprint position.
43 */
44 public static final int DEFAULT_MIN_NEAREST_FINGERPRINTS = -1;
45
46 /**
47 * Default maximum number of nearest fingerprints to search (no limit).
48 */
49 public static final int DEFAULT_MAX_NEAREST_FINGERPRINTS = -1;
50
51 /**
52 * Default exponent typically used on free space for path loss propagation in
53 * terms of distance. This value is used for free space environments.
54 */
55 public static final double DEFAULT_PATH_LOSS_EXPONENT = 2.0;
56
57 /**
58 * Located fingerprints containing RSSI readings.
59 */
60 protected List<? extends RssiFingerprintLocated<? extends RadioSource,
61 ? extends RssiReading<? extends RadioSource>, P>> locatedFingerprints;
62
63 /**
64 * Fingerprint containing readings at an unknown location.
65 * Readings need to belong to the same radio sources as those given at located
66 * fingerprints.
67 */
68 protected RssiFingerprint<? extends RadioSource,
69 ? extends RssiReading<? extends RadioSource>> fingerprint;
70
71 /**
72 * Minimum number of nearest fingerprints to search or -1 if there is no limit
73 * and all required fingerprints are initially used.
74 */
75 protected int minNearestFingerprints = DEFAULT_MIN_NEAREST_FINGERPRINTS;
76
77 /**
78 * Maximum number of nearest fingerprints to search or -1 if there is no limit and
79 * all provided fingerprints are used.
80 */
81 protected int maxNearestFingerprints = DEFAULT_MAX_NEAREST_FINGERPRINTS;
82
83 /**
84 * Path loss exponent to be used by default.
85 * This is typically used on free space for path loss propagation in
86 * terms of distance.
87 * On different environments path loss exponent might have different values:
88 * - Free space: 2.0
89 * - Urban Area: 2.7 to 3.5
90 * - Suburban Area: 3 to 5
91 * - Indoor (line-of-sight): 1.6 to 1.8
92 */
93 protected double pathLossExponent = DEFAULT_PATH_LOSS_EXPONENT;
94
95 /**
96 * Listener to be notified of events raised by this instance.
97 */
98 protected L listener;
99
100 /**
101 * Estimated inhomogeneous position coordinates.
102 */
103 protected double[] estimatedPositionCoordinates;
104
105 /**
106 * Radio sources provided along with located fingerprints containing
107 * their estimated locations.
108 */
109 protected List<RadioSourceLocated<P>> estimatedLocatedSources;
110
111 /**
112 * Nearest located fingerprints based on their RSSI readings respect to provided fingerprint.
113 * These are the fingerprints that are probably located close to the unknown location to be estimated,
114 * however their location is approximate due to errors on RSSI readings.
115 */
116 protected List<RssiFingerprintLocated<RadioSource, RssiReading<RadioSource>, P>> nearestFingerprints;
117
118 /**
119 * Indicates if this instance is locked.
120 */
121 protected boolean locked;
122
123 /**
124 * Constructor.
125 */
126 protected BaseFingerprintPositionAndRadioSourceEstimator() {
127 }
128
129 /**
130 * Constructor.
131 *
132 * @param listener listener in charge of handling events.
133 */
134 protected BaseFingerprintPositionAndRadioSourceEstimator(final L listener) {
135 this.listener = listener;
136 }
137
138 /**
139 * Constructor.
140 *
141 * @param locatedFingerprints located fingerprints containing RSSI readings.
142 * @param fingerprint fingerprint containing readings at an unknown location
143 * for provided located fingerprints.
144 * @throws IllegalArgumentException if either non located fingerprint or located
145 * fingerprints are null.
146 */
147 protected BaseFingerprintPositionAndRadioSourceEstimator(
148 final List<? extends RssiFingerprintLocated<? extends RadioSource,
149 ? extends RssiReading<? extends RadioSource>, P>> locatedFingerprints,
150 final RssiFingerprint<? extends RadioSource,
151 ? extends RssiReading<? extends RadioSource>> fingerprint) {
152 internalSetLocatedFingerprints(locatedFingerprints);
153 internalSetFingerprint(fingerprint);
154 }
155
156 /**
157 * Constructor.
158 *
159 * @param locatedFingerprints located fingerprints containing RSSI readings.
160 * @param fingerprint fingerprint containing readings at an unknown location
161 * for provided located fingerprints.
162 * @param listener listener in charge of handling events.
163 * @throws IllegalArgumentException if either non located fingerprint or located
164 * fingerprints are null.
165 */
166 protected BaseFingerprintPositionAndRadioSourceEstimator(
167 final List<? extends RssiFingerprintLocated<? extends RadioSource,
168 ? extends RssiReading<? extends RadioSource>, P>> locatedFingerprints,
169 final RssiFingerprint<? extends RadioSource,
170 ? extends RssiReading<? extends RadioSource>> fingerprint,
171 final L listener) {
172 this(listener);
173 internalSetLocatedFingerprints(locatedFingerprints);
174 internalSetFingerprint(fingerprint);
175 }
176
177 /**
178 * Gets located fingerprints containing RSSI readings.
179 *
180 * @return located fingerprints containing RSSI readings.
181 */
182 public List<RssiFingerprintLocated<RadioSource, RssiReading<RadioSource>, P>> getLocatedFingerprints() {
183 //noinspection unchecked
184 return (List<RssiFingerprintLocated<RadioSource, RssiReading<RadioSource>,P>>) locatedFingerprints;
185 }
186
187 /**
188 * Sets located fingerprints containing RSSI readings.
189 *
190 * @param locatedFingerprints located fingerprints containing RSSI readings.
191 * @throws LockedException if estimator is locked.
192 * @throws IllegalArgumentException if provided value is null or there are not enough
193 * fingerprints or readings within provided fingerprints (for 2D position estimation at
194 * least 2 readings are required in a single fingerprint, or at least 2 fingerprints
195 * at different locations containing a single reading are required. For 3D position
196 * estimation 3 reading in a single fingerprint, or 3 fingerprints containing a single
197 * reading or any combination resulting in at least 3 readings at different locations
198 * are required).
199 */
200 public void setLocatedFingerprints(
201 final List<? extends RssiFingerprintLocated<? extends RadioSource,
202 ? extends RssiReading<? extends RadioSource>, P>> locatedFingerprints) throws LockedException {
203 if (isLocked()) {
204 throw new LockedException();
205 }
206
207 internalSetLocatedFingerprints(locatedFingerprints);
208 }
209
210 /**
211 * Gets fingerprint containing readings at an unknown location for provided located
212 * fingerprints.
213 *
214 * @return fingerprint containing readings at an unknown location for provided located
215 * fingerprints.
216 */
217 public RssiFingerprint<RadioSource, RssiReading<RadioSource>> getFingerprint() {
218 //noinspection unchecked
219 return (RssiFingerprint<RadioSource, RssiReading<RadioSource>>) fingerprint;
220 }
221
222 /**
223 * Sets fingerprint containing readings at an unknown location for provided located fingerprints.
224 *
225 * @param fingerprint fingerprint containing readings at an unknown location for provided located fingerprints.
226 * @throws LockedException if estimator is locked.
227 * @throws IllegalArgumentException if provided value is null.
228 */
229 public void setFingerprint(
230 final RssiFingerprint<? extends RadioSource, ? extends RssiReading<? extends RadioSource>> fingerprint)
231 throws LockedException {
232 if (isLocked()) {
233 throw new LockedException();
234 }
235
236 internalSetFingerprint(fingerprint);
237 }
238
239 /**
240 * Get minimum number of nearest fingerprints to search.
241 *
242 * @return minimum number of nearest fingerprints, -1 indicates to initially use
243 * all fingerprints needed to estimate available radio sources.
244 */
245 public int getMinNearestFingerprints() {
246 return minNearestFingerprints;
247 }
248
249 /**
250 * Gets maximum number of nearest fingerprints to search.
251 *
252 * @return maximum number of nearest fingerprints, -1 indicates to use all available
253 * fingerprints.
254 */
255 public int getMaxNearestFingerprints() {
256 return maxNearestFingerprints;
257 }
258
259 /**
260 * Sets minimum and maximum number of nearest fingerprints to search.
261 * If minimum value is -1, then the minimum required number of fingerprints needed
262 * to estimate available radio sources is used.
263 * If maximum value is -1, then the problem is attempted to be solved until all
264 * available fingerprints are used.
265 *
266 * @param minNearestFingerprints minimum number of nearest fingerprints or -1.
267 * @param maxNearestFingerprints maximum number of nearest fingerprints or -1.
268 * @throws LockedException if estimator is locked.
269 * @throws IllegalArgumentException if minimum value is larger than maximum value (as
270 * long as it has a limit defined), or if maximum value is not negative when
271 * minimum one is less than 1, or if minimum value is zero.
272 */
273 public void setMinMaxNearestFingerprints(
274 final int minNearestFingerprints, final int maxNearestFingerprints) throws LockedException {
275 if (isLocked()) {
276 throw new LockedException();
277 }
278
279 internalSetMinMaxNearestFingerprints(minNearestFingerprints, maxNearestFingerprints);
280 }
281
282 /**
283 * Gets path loss exponent to be used by default.
284 * This is typically used on free space for path loss propagation in
285 * terms of distance.
286 * On different environments path loss exponent might have different values:
287 * - Free space: 2.0
288 * - Urban Area: 2.7 to 3.5
289 * - Suburban Area: 3 to 5
290 * - Indoor (line-of-sight): 1.6 to 1.8
291 *
292 * @return path loss exponent to be used by default.
293 */
294 public double getPathLossExponent() {
295 return pathLossExponent;
296 }
297
298 /**
299 * Sets path loss exponent to be used by default.
300 * This is typically used on free space for path loss propagation in
301 * terms of distance.
302 * On different environments path loss exponent might have different values:
303 * - Free space: 2.0
304 * - Urban Area: 2.7 to 3.5
305 * - Suburban Area: 3 to 5
306 * - Indoor (line-of-sight): 1.6 to 1.8
307 *
308 * @param pathLossExponent path loss exponent to be used by default.
309 * @throws LockedException if estimator is locked.
310 */
311 public void setPathLossExponent(final double pathLossExponent) throws LockedException {
312 if (isLocked()) {
313 throw new LockedException();
314 }
315 this.pathLossExponent = pathLossExponent;
316 }
317
318 /**
319 * Gets listener to be notified of events raised by this instance.
320 *
321 * @return listener to be notified of events raised by this instance.
322 */
323 public L getListener() {
324 return listener;
325 }
326
327 /**
328 * Sets listener to be notified of events raised by this instance.
329 *
330 * @param listener listener to be notified of events raised by this instance.
331 * @throws LockedException if estimator is locked.
332 */
333 public void setListener(final L listener) throws LockedException {
334 if (isLocked()) {
335 throw new LockedException();
336 }
337 this.listener = listener;
338 }
339
340 /**
341 * Gets estimated inhomogeneous position coordinates.
342 *
343 * @return estimated inhomogeneous position coordinates.
344 */
345 public double[] getEstimatedPositionCoordinates() {
346 return estimatedPositionCoordinates;
347 }
348
349 /**
350 * Gets estimated position and stores result into provided instance.
351 *
352 * @param estimatedPosition instance where estimated position will be stored.
353 */
354 public void getEstimatedPosition(final P estimatedPosition) {
355 if (estimatedPositionCoordinates != null) {
356 for (var i = 0; i < estimatedPositionCoordinates.length; i++) {
357 estimatedPosition.setInhomogeneousCoordinate(i, estimatedPositionCoordinates[i]);
358 }
359 }
360 }
361
362 /**
363 * Gets estimated position or null if not available yet.
364 *
365 * @return estimated position or null.
366 */
367 public P getEstimatedPosition() {
368 if (estimatedPositionCoordinates == null) {
369 return null;
370 }
371
372 final var result = createPoint();
373 getEstimatedPosition(result);
374 return result;
375 }
376
377 /**
378 * Gets radio sources provided along with located fingerprints containing
379 * their estimated locations.
380 *
381 * @return radio sources containing estimated locations.
382 */
383 public List<RadioSourceLocated<P>> getEstimatedLocatedSources() {
384 return estimatedLocatedSources;
385 }
386
387 /**
388 * Gets nearest found located fingerprints based on their RSSI readings respect to provided fingerprint.
389 * These are the fingerprints that are probably located close to the unknown location to be estimated,
390 * however their location is approximate due to errors on RSSI readings.
391 *
392 * @return nearest located fingerprints based on their RSSI readings or null if estimation has not been done yet.
393 */
394 public List<RssiFingerprintLocated<RadioSource, RssiReading<RadioSource>, P>> getNearestFingerprints() {
395 return nearestFingerprints;
396 }
397
398 /**
399 * Returns boolean indicating whether this estimator is locked because an estimation
400 * is already in progress.
401 *
402 * @return true if estimator is locked, false otherwise.
403 */
404 public boolean isLocked() {
405 return locked;
406 }
407
408 /**
409 * Indicates whether estimator is ready to find a solution.
410 *
411 * @return true if estimator is ready, false otherwise.
412 */
413 public boolean isReady() {
414 return locatedFingerprints != null && fingerprint != null;
415 }
416
417 /**
418 * Gets number of dimensions of points.
419 *
420 * @return number of dimensions of points.
421 */
422 public abstract int getNumberOfDimensions();
423
424 /**
425 * Starts estimation of position of unknown fingerprint and position of all radio sources
426 * associated to located fingerprints.
427 *
428 * @throws LockedException if estimator is locked.
429 * @throws NotReadyException if estimator is not ready.
430 * @throws FingerprintEstimationException if estimation fails for some other reason.
431 */
432 public abstract void estimate() throws LockedException, NotReadyException, FingerprintEstimationException;
433
434 /**
435 * Create a point.
436 *
437 * @return point to be created.
438 */
439 protected abstract P createPoint();
440
441 /**
442 * Internally sets located fingerprints containing RSSI readings.
443 *
444 * @param locatedFingerprints located fingerprints containing RSSI readings.
445 * @throws IllegalArgumentException if provided value is null.
446 */
447 private void internalSetLocatedFingerprints(
448 final List<? extends RssiFingerprintLocated<? extends RadioSource,
449 ? extends RssiReading<? extends RadioSource>, P>> locatedFingerprints) {
450 if (locatedFingerprints == null) {
451 throw new IllegalArgumentException();
452 }
453
454 this.locatedFingerprints = locatedFingerprints;
455 }
456
457 /**
458 * Internally sets fingerprint containing readings at an unknown location for provided
459 * located fingerprints.
460 *
461 * @param fingerprint fingerprint containing readings at an unknown location for
462 * provided located fingerprints.
463 * @throws IllegalArgumentException if provided value is null.
464 */
465 private void internalSetFingerprint(
466 final RssiFingerprint<? extends RadioSource, ? extends RssiReading<? extends RadioSource>> fingerprint) {
467 if (fingerprint == null) {
468 throw new IllegalArgumentException();
469 }
470
471 this.fingerprint = fingerprint;
472 }
473
474 /**
475 * Sets minimum and maximum number of nearest fingerprints to search.
476 *
477 * @param minNearestFingerprints minimum number of nearest fingerprints.
478 * @param maxNearestFingerprints maximum number of nearest fingerprints.
479 * @throws IllegalArgumentException if minimum value is larger than maximum value (as
480 * long as it has a limit defined), or if maximum value is not negative when
481 * minimum one is less than 1.
482 */
483 @SuppressWarnings("Duplicates")
484 private void internalSetMinMaxNearestFingerprints(
485 final int minNearestFingerprints, final int maxNearestFingerprints) {
486 if (minNearestFingerprints == 0 || (minNearestFingerprints < 1 && maxNearestFingerprints >= 0)
487 || (maxNearestFingerprints >= 0 && minNearestFingerprints > maxNearestFingerprints)) {
488 throw new IllegalArgumentException();
489 }
490
491 this.minNearestFingerprints = minNearestFingerprints;
492 this.maxNearestFingerprints = maxNearestFingerprints;
493 }
494 }