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;
17
18 import java.util.List;
19
20 /**
21 * Contains RSSI readings from several radio sources for an unknown location to be
22 * determined.
23 *
24 * @param <R> a {@link RssiReading} type.
25 * @param <S> a {@link RadioSource} type.
26 */
27 public class RssiFingerprint<S extends RadioSource, R extends RssiReading<S>> extends Fingerprint<S, R> {
28
29 /**
30 * Constructor.
31 */
32 public RssiFingerprint() {
33 }
34
35 /**
36 * Constructor.
37 *
38 * @param readings non-located RSSI readings.
39 * @throws IllegalArgumentException if provided readings is null.
40 */
41 public RssiFingerprint(final List<R> readings) {
42 super(readings);
43 }
44
45 /**
46 * Gets Euclidean distance of signal readings from another fingerprint.
47 *
48 * @param otherFingerprint other fingerprint to compare.
49 * @return Euclidean distance of signal readings from another fingerprint.
50 */
51 public double distanceTo(final RssiFingerprint<S, R> otherFingerprint) {
52 return Math.sqrt(sqrDistanceTo(otherFingerprint));
53 }
54
55 /**
56 * Gets squared Euclidean distance of signal readings from another fingerprint.
57 *
58 * @param otherFingerprint other fingerprint to compare.
59 * @return squared Euclidean distance of signal readings from another
60 * fingerprint.
61 */
62 @SuppressWarnings("Duplicates")
63 public double sqrDistanceTo(final RssiFingerprint<S, R> otherFingerprint) {
64 if (otherFingerprint == null) {
65 return Double.MAX_VALUE;
66 }
67
68 final var otherReadings = otherFingerprint.getReadings();
69 var numAccessPoints = 0;
70 var result = 0.0;
71 double diff;
72 for (final var reading : readings) {
73 for (final var otherReading : otherReadings) {
74 if (reading.hasSameSource(otherReading)) {
75 diff = reading.getRssi() - otherReading.getRssi();
76 result += diff * diff;
77 numAccessPoints++;
78 }
79 }
80 }
81
82 if (numAccessPoints == 0) {
83 return Double.MAX_VALUE;
84 }
85
86 return result;
87 }
88
89 /**
90 * Gets average RSSI (received signal strength indicator) of all readings contained in this fingerprint
91 * expressed in dB's.
92 *
93 * @return average RSSI of all readings.
94 */
95 public double getMeanRssi() {
96 if (readings == null || readings.isEmpty()) {
97 return Double.MAX_VALUE;
98 }
99
100 var result = 0.0;
101 for (final var reading : readings) {
102 result += reading.getRssi() / readings.size();
103 }
104
105 return result;
106 }
107
108 /**
109 * Gets Euclidean distance of signal readings from another fingerprint.
110 *
111 * @param otherFingerprint other fingerprint to compare.
112 * @return Euclidean distance of signal readings from another fingerprint.
113 */
114 public double noMeanDistanceTo(final RssiFingerprint<S, R> otherFingerprint) {
115 return Math.sqrt(noMeanSqrDistanceTo(otherFingerprint));
116 }
117
118 /**
119 * Gets squared Euclidean distance of signal readings with mean RSSI removed from another fingerprint.
120 * Mean RSSI's are taken into account so that bias effects introduced by different device's hardware is
121 * partially removed.
122 *
123 * @param otherFingerprint other fingerprint to compare.
124 * @return squared Euclidean distance of signal readings from another
125 * fingerprint with average RSSI's removed.
126 */
127 @SuppressWarnings("Duplicates")
128 public double noMeanSqrDistanceTo(final RssiFingerprint<S, R> otherFingerprint) {
129 if (otherFingerprint == null) {
130 return Double.MAX_VALUE;
131 }
132
133 final var otherReadings = otherFingerprint.getReadings();
134 var numAccessPoints = 0;
135 var avgRssiThis = 0.0;
136 var avgRssiOther = 0.0;
137 for (final var reading : readings) {
138 for (final var otherReading : otherReadings) {
139 if (reading.hasSameSource(otherReading)) {
140 avgRssiThis += reading.getRssi();
141 avgRssiOther += otherReading.getRssi();
142
143 numAccessPoints++;
144 }
145 }
146 }
147
148 if (numAccessPoints == 0) {
149 return Double.MAX_VALUE;
150 }
151
152 avgRssiThis /= numAccessPoints;
153 avgRssiOther /= numAccessPoints;
154
155 var result = 0.0;
156 for (final var reading : readings) {
157 for (final var otherReading : otherReadings) {
158 if (reading.hasSameSource(otherReading)) {
159 final var diff = (reading.getRssi() - avgRssiThis) - (otherReading.getRssi() - avgRssiOther);
160 result += diff * diff;
161 }
162 }
163 }
164
165 return result;
166 }
167 }