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.radiosource;
17  
18  import com.irurueta.geometry.Accuracy2D;
19  import com.irurueta.geometry.InhomogeneousPoint2D;
20  import com.irurueta.geometry.Point2D;
21  import com.irurueta.navigation.LockedException;
22  import com.irurueta.navigation.indoor.Beacon;
23  import com.irurueta.navigation.indoor.BeaconLocated2D;
24  import com.irurueta.navigation.indoor.RadioSource;
25  import com.irurueta.navigation.indoor.RadioSourceLocated;
26  import com.irurueta.navigation.indoor.RangingReadingLocated;
27  import com.irurueta.navigation.indoor.WifiAccessPoint;
28  import com.irurueta.navigation.indoor.WifiAccessPointLocated2D;
29  import com.irurueta.navigation.lateration.HomogeneousLinearLeastSquaresLateration2DSolver;
30  import com.irurueta.navigation.lateration.InhomogeneousLinearLeastSquaresLateration2DSolver;
31  import com.irurueta.navigation.lateration.NonLinearLeastSquaresLateration2DSolver;
32  
33  import java.util.List;
34  
35  /**
36   * Estimates position of a radio source (e.g. Wi-Fi access point or bluetooth beacon)
37   * by using ranging measurements.
38   * Ranging measurements can be obtained by protocols such as ieee 802.11mc (Wi-Fi RTT) which
39   * measures travel time of signal and converts the result into distances by taking into
40   * account the speed of light as the propagation speed.
41   *
42   * @param <S> a {@link RadioSource} type.
43   */
44  @SuppressWarnings("DuplicatedCode")
45  public class RangingRadioSourceEstimator2D<S extends RadioSource> extends RangingRadioSourceEstimator<S, Point2D> {
46  
47      /**
48       * Constructor.
49       */
50      public RangingRadioSourceEstimator2D() {
51          super();
52      }
53  
54      /**
55       * Constructor.
56       * Sets radio signal ranging readings belonging to the same radio source.
57       *
58       * @param readings radio signal ranging readings belonging to the same radio source.
59       * @throws IllegalArgumentException if readings are not valid.
60       */
61      public RangingRadioSourceEstimator2D(final List<? extends RangingReadingLocated<S, Point2D>> readings) {
62          super(readings);
63      }
64  
65      /**
66       * Constructor.
67       *
68       * @param listener listener in charge of attending events raised by this instance.
69       */
70      public RangingRadioSourceEstimator2D(final RangingRadioSourceEstimatorListener<S, Point2D> listener) {
71          super(listener);
72      }
73  
74      /**
75       * Constructor.
76       * Sets radio signal readings belonging to the same radio source.
77       *
78       * @param readings radio signal readings belonging to the same radio source.
79       * @param listener listener in charge of attending events raised by this instance.
80       * @throws IllegalArgumentException if readings are not valid.
81       */
82      public RangingRadioSourceEstimator2D(
83              final List<? extends RangingReadingLocated<S, Point2D>> readings,
84              final RangingRadioSourceEstimatorListener<S, Point2D> listener) {
85          super(readings, listener);
86      }
87  
88      /**
89       * Constructor.
90       *
91       * @param initialPosition initial position to start the estimation or radio
92       *                        source position.
93       */
94      public RangingRadioSourceEstimator2D(final Point2D initialPosition) {
95          super(initialPosition);
96      }
97  
98      /**
99       * Constructor.
100      * Sets radio signal readings belonging to the same radio source.
101      *
102      * @param readings        radio signal readings belonging to the same radio source.
103      * @param initialPosition initial position to start the estimation of radio
104      *                        source position.
105      * @throws IllegalArgumentException if readings are not valid.
106      */
107     public RangingRadioSourceEstimator2D(
108             final List<? extends RangingReadingLocated<S, Point2D>> readings, final Point2D initialPosition) {
109         super(readings, initialPosition);
110     }
111 
112     /**
113      * Constructor.
114      *
115      * @param initialPosition initial position to start the estimation of radio
116      *                        source position.
117      * @param listener        listener in charge of attending events raised by this instance.
118      */
119     public RangingRadioSourceEstimator2D(
120             final Point2D initialPosition, final RangingRadioSourceEstimatorListener<S, Point2D> listener) {
121         super(initialPosition, listener);
122     }
123 
124     /**
125      * Constructor.
126      * Sets radio signal ranging readings belonging to the same radio source.
127      *
128      * @param readings        radio signal ranging readings belonging to the same radio source.
129      * @param initialPosition initial position to start the estimation of radio source
130      *                        position.
131      * @param listener        listener in charge of attending events raised by this instance.
132      * @throws IllegalArgumentException if readings are not valid.
133      */
134     public RangingRadioSourceEstimator2D(
135             final List<? extends RangingReadingLocated<S, Point2D>> readings, final Point2D initialPosition,
136             final RangingRadioSourceEstimatorListener<S, Point2D> listener) {
137         super(readings, initialPosition, listener);
138     }
139 
140     /**
141      * Gets minimum required number of readings to estimate position of radio source,
142      * which is 3 readings.
143      *
144      * @return minimum required number of readings.
145      */
146     @Override
147     public int getMinReadings() {
148         return Point2D.POINT2D_INHOMOGENEOUS_COORDINATES_LENGTH + 1;
149     }
150 
151     /**
152      * Gets number of dimensions of position points.
153      * This is always 2.
154      *
155      * @return number of dimensions of position points.
156      */
157     @Override
158     public int getNumberOfDimensions() {
159         return Point2D.POINT2D_INHOMOGENEOUS_COORDINATES_LENGTH;
160     }
161 
162     /**
163      * Gets estimated radio source 2D position.
164      *
165      * @return estimated radio source 2D position.
166      */
167     @Override
168     public Point2D getEstimatedPosition() {
169         if (estimatedPositionCoordinates == null) {
170             return null;
171         }
172 
173         final var result = new InhomogeneousPoint2D();
174         getEstimatedPosition(result);
175         return result;
176     }
177 
178     /**
179      * Gets estimated located radio source.
180      *
181      * @return estimated located radio source or null.
182      */
183     @Override
184     @SuppressWarnings("unchecked")
185     public RadioSourceLocated<Point2D> getEstimatedRadioSource() {
186         final var readings = getReadings();
187         if (readings == null || readings.isEmpty()) {
188             return null;
189         }
190         final var source = readings.get(0).getSource();
191 
192         final var estimatedPosition = getEstimatedPosition();
193         if (estimatedPosition == null) {
194             return null;
195         }
196 
197         final var estimatedPositionCovariance = getEstimatedPositionCovariance();
198 
199         if (source instanceof WifiAccessPoint accessPoint) {
200             return new WifiAccessPointLocated2D(accessPoint.getBssid(), accessPoint.getFrequency(),
201                     accessPoint.getSsid(), estimatedPosition, estimatedPositionCovariance);
202         } else if (source instanceof Beacon beacon) {
203             return new BeaconLocated2D(beacon.getIdentifiers(), beacon.getTransmittedPower(), beacon.getFrequency(),
204                     beacon.getBluetoothAddress(), beacon.getBeaconTypeCode(), beacon.getManufacturer(),
205                     beacon.getServiceUuid(), beacon.getBluetoothName(), estimatedPosition, estimatedPositionCovariance);
206         } else {
207             return null;
208         }
209     }
210 
211     /**
212      * Builds an instance of a linear lateration solver if needed.
213      */
214     @Override
215     protected void buildLinearSolverIfNeeded() {
216         if (initialPosition == null || !nonLinearSolverEnabled) {
217             if (useHomogeneousLinearSolver && homogeneousLinearSolver == null) {
218                 homogeneousLinearSolver = new HomogeneousLinearLeastSquaresLateration2DSolver();
219             }
220             if (!useHomogeneousLinearSolver && inhomogeneousLinearSolver == null) {
221                 inhomogeneousLinearSolver = new InhomogeneousLinearLeastSquaresLateration2DSolver();
222             }
223         }
224     }
225 
226     /**
227      * Builds an instance of a non-linear lateration solver if needed.
228      */
229     @Override
230     protected void buildNonLinearSolverIfNeeded() {
231         if (nonLinearSolver == null && nonLinearSolverEnabled) {
232             nonLinearSolver = new NonLinearLeastSquaresLateration2DSolver();
233         }
234     }
235 
236     /**
237      * Build an instance of accuracy if needed.
238      */
239     @Override
240     protected void buildAccuracyIfNeeded() {
241         if (accuracy == null && useReadingPositionCovariances) {
242             accuracy = new Accuracy2D();
243 
244             // to work with standard deviations, we need a unitary factor
245             accuracy.setStandardDeviationFactor(1.0);
246         }
247     }
248 
249 
250     /**
251      * Sets positions, distances and standard deviations of distances on internal
252      * lateration solver.
253      *
254      * @param positions                  positions to be set.
255      * @param distances                  distances to be set.
256      * @param distanceStandardDeviations standard deviations of distances to be set or
257      *                                   null.
258      * @throws LockedException if solvers are locked.
259      */
260     @Override
261     protected void setPositionsDistancesAndDistanceStandardDeviations(
262             final List<Point2D> positions, final List<Double> distances, final List<Double> distanceStandardDeviations)
263             throws LockedException {
264         final var size = positions.size();
265         Point2D[] positionsArray = new InhomogeneousPoint2D[size];
266         positionsArray = positions.toArray(positionsArray);
267 
268         final var distancesArray = new double[size];
269         final var distanceStandardDeviationsArray = new double[size];
270         for (var i = 0; i < size; i++) {
271             distancesArray[i] = distances.get(i);
272             distanceStandardDeviationsArray[i] = distanceStandardDeviations.get(i);
273         }
274 
275         if (initialPosition == null || !nonLinearSolverEnabled) {
276             if (homogeneousLinearSolver != null) {
277                 homogeneousLinearSolver.setPositionsAndDistances(positionsArray, distancesArray);
278             }
279             if (inhomogeneousLinearSolver != null) {
280                 inhomogeneousLinearSolver.setPositionsAndDistances(positionsArray, distancesArray);
281             }
282         }
283 
284         if (nonLinearSolver != null && nonLinearSolverEnabled) {
285             nonLinearSolver.setPositionsDistancesAndStandardDeviations(positionsArray, distancesArray,
286                     distanceStandardDeviationsArray);
287         }
288     }
289 }