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;
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 }