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.lateration;
17  
18  import com.irurueta.geometry.Point;
19  import com.irurueta.navigation.LockedException;
20  import com.irurueta.navigation.NotReadyException;
21  
22  /**
23   * Solves the lateration problem.
24   * This is a formulation for a nonlinear least squares optimizer.
25   * This class is base on the implementation found at:
26   * <a href="https://github.com/lemmingapex/trilateration">https://github.com/lemmingapex/trilateration</a>
27   *
28   * @param <P> a {@link Point} type.
29   */
30  @SuppressWarnings("Duplicates")
31  public abstract class LaterationSolver<P extends Point<?>> {
32  
33      /**
34       * Minimum allowed distance for a given circle or sphere.
35       */
36      public static final double EPSILON = 1e-7;
37  
38      /**
39       * Known positions of static nodes.
40       */
41      protected P[] positions;
42  
43      /**
44       * Euclidean distances from static nodes to mobile node.
45       */
46      protected double[] distances;
47  
48      /**
49       * Listener to be notified of events raised by this instance.
50       */
51      protected LaterationSolverListener<P> listener;
52  
53      /**
54       * Estimated inhomogeneous position coordinates.
55       */
56      protected double[] estimatedPositionCoordinates;
57  
58      /**
59       * Indicates if this instance is locked because lateration is being solved.
60       */
61      protected boolean locked;
62  
63      /**
64       * Constructor.
65       */
66      protected LaterationSolver() {
67      }
68  
69      /**
70       * Constructor.
71       * Sets known positions and Euclidean distances.
72       *
73       * @param positions known positions of static nodes.
74       * @param distances euclidean distances from static nodes to mobile node.
75       * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
76       *                                  length is smaller than required (3 for 2D points or 4 for 3D points).
77       */
78      protected LaterationSolver(final P[] positions, final double[] distances) {
79          internalSetPositionsAndDistances(positions, distances);
80      }
81  
82      /**
83       * Constructor.
84       *
85       * @param listener listener to be notified of events raised by this instance.
86       */
87      protected LaterationSolver(final LaterationSolverListener<P> listener) {
88          this.listener = listener;
89      }
90  
91      /**
92       * Constructor.
93       * Sets known positions and Euclidean distances.
94       *
95       * @param positions known positions of static nodes.
96       * @param distances euclidean distances from static nodes to mobile node.
97       * @param listener  listener to be notified of events raised by this instance.
98       * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
99       *                                  length is smaller than required (3 for 2D points or 4 for 3D points).
100      */
101     protected LaterationSolver(
102             final P[] positions, final double[] distances, final LaterationSolverListener<P> listener) {
103         this(positions, distances);
104         this.listener = listener;
105     }
106 
107     /**
108      * Gets listener to be notified of events raised by this instance.
109      *
110      * @return listener to be notified of events raised by this instance.
111      */
112     public LaterationSolverListener<P> getListener() {
113         return listener;
114     }
115 
116     /**
117      * Sets listener to be notified of events raised by this instance.
118      *
119      * @param listener listener to be notified of events raised by this instance.
120      * @throws LockedException if instance is busy solving the lateration problem.
121      */
122     public void setListener(final LaterationSolverListener<P> listener) throws LockedException {
123         if (isLocked()) {
124             throw new LockedException();
125         }
126         this.listener = listener;
127     }
128 
129     /**
130      * Gets known positions of static nodes.
131      *
132      * @return known positions of static nodes.
133      */
134     public P[] getPositions() {
135         return positions;
136     }
137 
138     /**
139      * Gets euclidean distances from static nodes to mobile node.
140      *
141      * @return euclidean distances from static nodes to mobile node.
142      */
143     public double[] getDistances() {
144         return distances;
145     }
146 
147     /**
148      * Indicates whether solver is ready to find a solution.
149      *
150      * @return true if solver is ready, false otherwise.
151      */
152     public boolean isReady() {
153         return positions != null && distances != null && positions.length >= getMinRequiredPositionsAndDistances();
154     }
155 
156     /**
157      * Returns boolean indicating if solver is locked because estimation is under
158      * progress.
159      *
160      * @return true if solver is locked, false otherwise.
161      */
162     public boolean isLocked() {
163         return locked;
164     }
165 
166     /**
167      * Sets known positions and Euclidean distances.
168      * If any distance value is zero or negative, it will be fixed assuming an EPSILON value.
169      *
170      * @param positions known positions of static nodes.
171      * @param distances euclidean distances from static nodes to mobile node.
172      * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
173      *                                  length is smaller than required (2 points).
174      * @throws LockedException          if instance is busy solving the lateration problem.
175      */
176     public void setPositionsAndDistances(
177             final P[] positions, final double[] distances) throws LockedException {
178         if (isLocked()) {
179             throw new LockedException();
180         }
181         internalSetPositionsAndDistances(positions, distances);
182     }
183 
184     /**
185      * Gets estimated inhomogeneous position coordinates.
186      *
187      * @return estimated inhomogeneous position coordinates.
188      */
189     public double[] getEstimatedPositionCoordinates() {
190         return estimatedPositionCoordinates;
191     }
192 
193     /**
194      * Gets estimated position and stores result into provided instance.
195      *
196      * @param estimatedPosition instance where estimated position will be stored.
197      */
198     public void getEstimatedPosition(final P estimatedPosition) {
199         if (estimatedPositionCoordinates != null) {
200             for (var i = 0; i < estimatedPositionCoordinates.length; i++) {
201                 estimatedPosition.setInhomogeneousCoordinate(i, estimatedPositionCoordinates[i]);
202             }
203         }
204     }
205 
206     /**
207      * Gets estimated position.
208      *
209      * @return estimated position.
210      */
211     public abstract P getEstimatedPosition();
212 
213     /**
214      * Gets number of dimensions of provided points.
215      *
216      * @return number of dimensions of provided points.
217      */
218     public abstract int getNumberOfDimensions();
219 
220     /**
221      * Solves the lateration problem.
222      *
223      * @throws LaterationException if lateration fails.
224      * @throws NotReadyException   is solver is not ready.
225      * @throws LockedException     if instance is busy solving the lateration problem.
226      */
227     public abstract void solve() throws LaterationException, NotReadyException, LockedException;
228 
229     /**
230      * Gets lateration solver type.
231      *
232      * @return lateration solver type.
233      */
234     public abstract LaterationSolverType getType();
235 
236     /**
237      * Minimum required number of positions and distances.
238      * This value will depend on actual implementation and whether we are solving a 2D or 3D problem.
239      *
240      * @return minimum required number of positions and distances.
241      */
242     public abstract int getMinRequiredPositionsAndDistances();
243 
244     /**
245      * Internally sets known positions and Euclidean distances.
246      * If any distance value is zero or negative, it will be fixed assuming an EPSILON value.
247      *
248      * @param positions known positions of static nodes.
249      * @param distances euclidean distances from static nodes to mobile node.
250      * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
251      *                                  length is smaller than required (2 points).
252      */
253     protected void internalSetPositionsAndDistances(final P[] positions, final double[] distances) {
254         if (positions == null || distances == null) {
255             throw new IllegalArgumentException();
256         }
257 
258         if (positions.length < getMinRequiredPositionsAndDistances()) {
259             throw new IllegalArgumentException();
260         }
261 
262         if (positions.length != distances.length) {
263             throw new IllegalArgumentException();
264         }
265 
266         this.positions = positions;
267         this.distances = distances;
268 
269         // fix distances if needed
270         for (var i = 0; i < this.distances.length; i++) {
271             if (this.distances[i] < EPSILON) {
272                 this.distances[i] = EPSILON;
273             }
274         }
275     }
276 }