multilateration/
lib.rs

1//! [![github]](https://github.com/ebakoba/multilateration) [![crates-io]](https://crates.io/crates/multilateration) [![docs-rs]](https://docs.rs/multilateration)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
6//!
7//! <br>
8//!
9//! This is a multilateration library implemented in Rust, which is loosly
10//! based on Java [trilateration library](https://github.com/lemmingapex/trilateration).
11//!
12//!
13//! <br>
14//!
15//! # Details
16//!
17//! - It exposes one function that uses a vector of `Measurement` struct as an input.
18//!   Output will be the tuple struct called `Point` which contains the output coordinates
19//!   as a vector in the first tuple slot
20//!   
21//!
22//!   ```
23//!   use multilateration::{multilaterate, Measurement, Point};
24//!
25//!   fn main() -> Result<()> {
26//!     let measurements = vec![
27//!       Measurement::new(Point(vec![1.0, 1.0, 1.0]), 1.0),
28//!       Measurement::new(Point(vec![3.0, 1.0, 1.0]), 1.0),
29//!       Measurement::new(Point(vec![2.0, 2.0, 1.0]), 1.0),
30//!     ];
31//!
32//!     let coordinates = multilaterate(measurements).unwrap().0;
33//!     println!("Coordinates are: {:?}", coordinates);
34//!     Ok(())
35//!   }
36//!   ```
37//!
38//!   ```console
39//!   Coordinates are: [2.0, 1.0000157132198315, 0.9943941804736127]
40//!   ```
41//!
42//! <br>
43//!
44//! # Error conditions
45//!
46//! - Points have different dimensions
47//! 
48//! ```
49//!   use multilateration::{multilaterate, Measurement, Point};
50//!
51//!   fn main() -> Result<()> {
52//!     let measurements = vec![
53//!       Measurement::new(Point(vec![1.0, 1.0]), 1.0),
54//!       Measurement::new(Point(vec![3.0, 1.0, 1.0]), 1.0),
55//!       Measurement::new(Point(vec![2.0, 2.0, 1.0]), 1.0),
56//!     ];
57//!
58//!     let result = multilaterate(measurements);
59//!     println!("Result is: {:?}", result);
60//!     Ok(())
61//!   }
62//!   ```
63//!
64//!   ```console
65//!   Result is: Err(All points must have the same dimensions)
66//!   ```
67//! - Points have no dimensions
68//! 
69//! ```
70//!   use multilateration::{multilaterate, Measurement, Point};
71//!
72//!   fn main() -> Result<()> {
73//!     let measurements = vec![
74//!       Measurement::new(Point(vec![]), 1.0),
75//!       Measurement::new(Point(vec![]), 1.0),
76//!       Measurement::new(Point(vec![]), 1.0),
77//!     ];
78//!
79//!     let result = multilaterate(measurements);
80//!     println!("Result is: {:?}", result);
81//!     Ok(())
82//!   }
83//!   ```
84//!
85//!   ```console
86//!   Result is: Err(Points must contain at least one dimension)
87//!   ```
88
89
90use mathru::optimization::{
91    LevenbergMarquardt,
92    Optim
93};
94use mathru::algebra::linear::vector::vector::Vector;
95use mathru::algebra::linear::matrix::matrix::Matrix;
96use anyhow::{Result, anyhow, bail};
97
98struct MultilaterationFunction {
99    measurements: Vec<Measurement>,
100}
101
102impl MultilaterationFunction {
103    pub fn new(measurements: Vec<Measurement>) -> MultilaterationFunction {
104        MultilaterationFunction{
105            measurements,
106        }
107    }
108
109    pub fn estimate_intial_point(&self) -> Point {
110        let position_dimensions = self.measurements[0].point.0.len();
111        let number_of_measurements = self.measurements.len();
112
113        let mut initial_position = vec![0f64; position_dimensions];
114        for i in 0..number_of_measurements {
115            for j in 0..position_dimensions {
116                initial_position[j] = self
117                    .measurements[i].point.0[j];
118            }
119        }
120        for i in 0..position_dimensions {
121            initial_position[i] /= number_of_measurements as f64;
122        }
123
124        Point(initial_position)
125    }
126}
127
128impl Optim<f64> for MultilaterationFunction {
129    fn eval(&self, input: &Vector<f64>) -> Vector<f64> {
130        let mut result = vec![0f64; self.measurements.len()];
131
132        for i in 0..self.measurements.len() {
133            result[i] = 0f64;
134            for j in 0..input.clone().convert_to_vec().len() {
135                result[i] +=
136                    f64::powf(
137                        *input.get(j) -
138                        self.measurements[i].point.0[j],
139                    2f64
140                );
141            }
142            result[i] -= f64::powf(self.measurements[i].distance, 2f64);
143        }
144
145        Vector::new_column(self.measurements.len(), result)
146    }
147
148    fn jacobian(&self, input: &Vector<f64>) -> Matrix<f64> {
149        let input_length = input.clone().convert_to_vec().len();
150        let data = vec![
151            0f64;
152            self.measurements.len() * input_length
153        ];
154        let mut matrix = Matrix::new(
155            self.measurements.len(),
156            input_length,
157            data
158        );
159
160        for i in 0..self.measurements.len() {
161            for j in 0..input_length {
162                *matrix.get_mut(i, j) =
163                    2f64 * input.get(j) - 
164                    2f64 * self.measurements[i].point.0[j];
165            }
166        }
167
168        matrix
169    }
170}
171
172#[derive(Debug, Clone)]
173pub struct Point(pub Vec<f64>);
174
175#[derive(Debug)]
176pub struct Measurement {
177    point: Point,
178    distance: f64
179}
180
181impl Measurement {
182    pub fn new(point: Point, distance: f64) -> Measurement {
183        Measurement {
184            point,
185            distance
186        }
187    }
188}
189
190fn validate_measurements(measurements: &[Measurement]) -> Result<()> {
191    let point_dimensions: Vec<usize> = measurements.iter().map(|measurement| measurement.point.0.len()).collect();
192    let min_length = *point_dimensions.iter().min().ok_or(anyhow!("Failed to calculate minimal dimension"))?;
193    let max_length = *point_dimensions.iter().max().ok_or(anyhow!("Failed to calculate maximal dimension"))?;
194
195    if min_length != max_length {
196        bail!("All points must have the same dimensions");
197    }
198    if min_length < 1usize {
199        bail!("Points must contain at least one dimension");
200    }
201    Ok(())
202}
203
204pub fn multilaterate(
205    measurements: Vec<Measurement>
206) -> Result<Point> {
207    validate_measurements(&measurements)?;
208    let multilateration_function = MultilaterationFunction::new(measurements);
209    let optimization = LevenbergMarquardt::new(1000, -1f64, 1f64);
210    let initial_point = multilateration_function.estimate_intial_point();
211    let result = optimization.minimize(
212        &multilateration_function,
213        &Vector::new_column(
214            initial_point.0.len(),
215            initial_point.0
216        )
217    ).map_err(|_| anyhow!("Failed to calculate a result"))?;
218
219    Ok(Point(result.arg().convert_to_vec()))
220}
221
222#[cfg(test)]
223mod tests {
224    use super::Point;
225    use super::Measurement;
226    use super::multilaterate;
227
228    fn is_in_delta(
229        delta: f64,
230        value: f64,
231        comparison: f64
232    ) -> bool {
233        value >= (comparison - delta) && value <= (comparison + delta)
234    }
235
236    #[test]
237    fn multilat_1() {
238        let measurements = vec![
239            Measurement {
240                point: Point(vec![5.0, -6.0]),
241                distance: 8.06
242            },
243            Measurement {
244                point: Point(vec![13.0, -15.0]),
245                distance: 13.97
246            },
247            Measurement {
248                point: Point(vec![21.0, -3.0]),
249                distance: 23.32
250            },
251            Measurement {
252                point: Point(vec![12.4, -21.2]),
253                distance: 15.31
254            },
255        ];
256
257        let result = multilaterate(measurements).unwrap().0;
258        assert_eq!(result.len(), 2);
259        let delta = 1f64;
260        assert!(is_in_delta(delta, result[0], -0.6));
261        assert!(is_in_delta(delta, result[1], -11.8));
262    }
263
264    #[test]
265    fn multilat_2() {
266        let measurements = vec![
267            Measurement {
268                point: Point(vec![1.0, 1.0]),
269                distance: 0.5
270            },
271            Measurement {
272                point: Point(vec![3.0, 1.0]),
273                distance: 0.5
274            },
275            Measurement {
276                point: Point(vec![2.0, 2.0]),
277                distance: 0.5
278            }
279        ];
280
281        let result = multilaterate(measurements).unwrap().0;
282        assert_eq!(result.len(), 2);
283        let delta = 0.4;
284        assert!(is_in_delta(delta, result[0], 2.0));
285        assert!(is_in_delta(delta, result[1], 1.0));
286    }
287
288    #[test]
289    fn multilat_3() {
290        let measurements = vec![
291            Measurement {
292                point: Point(vec![1.0, 1.0]),
293                distance: 2.0
294            },
295            Measurement {
296                point: Point(vec![3.0, 1.0]),
297                distance: 2.0
298            },
299            Measurement {
300                point: Point(vec![2.0, 2.0]),
301                distance: 2.0
302            }
303        ];
304
305        let result = multilaterate(measurements).unwrap().0;
306        assert_eq!(result.len(), 2);
307        let delta = 2.0;
308        assert!(is_in_delta(delta, result[0], 2.0));
309        assert!(is_in_delta(delta, result[1], 1.0));
310    }
311
312    #[test]
313    fn multilat_4() {
314        let measurements = vec![
315            Measurement {
316                point: Point(vec![1.0, 1.0]),
317                distance: 1.0
318            },
319            Measurement {
320                point: Point(vec![1.0, 1.0]),
321                distance: 1.0
322            },
323            Measurement {
324                point: Point(vec![3.0, 1.0]),
325                distance: 1.0
326            }
327        ];
328
329        let result = multilaterate(measurements).unwrap().0;
330        assert_eq!(result.len(), 2);
331        let delta = 0.5;
332        assert!(is_in_delta(delta, result[0], 2.0));
333        assert!(is_in_delta(delta, result[1], 1.0));
334    }
335
336    #[test]
337    fn multilat_5() {
338        let measurements = vec![
339            Measurement {
340                point: Point(vec![1.0, 1.0]),
341                distance: 1.0
342            },
343            Measurement {
344                point: Point(vec![3.0, 1.0]),
345                distance: 1.0
346            }
347        ];
348
349        let result = multilaterate(measurements).unwrap().0;
350        assert_eq!(result.len(), 2);
351        let delta = 0.5;
352        assert!(is_in_delta(delta, result[0], 2.0));
353        assert!(is_in_delta(delta, result[1], 1.0));
354    }
355
356    #[test]
357    fn multilat_6() {
358        let measurements = vec![
359            Measurement {
360                point: Point(vec![1.0, 1.0]),
361                distance: 0.9
362            },
363            Measurement {
364                point: Point(vec![3.0, 1.0]),
365                distance: 1.0
366            },
367            Measurement {
368                point: Point(vec![2.0, 2.0]),
369                distance: 1.0
370            }
371        ];
372
373        let result = multilaterate(measurements).unwrap().0;
374        assert_eq!(result.len(), 2);
375        let delta = 0.1;
376        assert!(is_in_delta(delta, result[0], 2.0));
377        assert!(is_in_delta(delta, result[1], 1.0));
378    }
379
380    #[test]
381    fn multilat_7() {
382        let measurements = vec![
383            Measurement {
384                point: Point(vec![1.0, 1.0, 1.0]),
385                distance: 1.0
386            },
387            Measurement {
388                point: Point(vec![3.0, 1.0, 1.0]),
389                distance: 1.0
390            },
391            Measurement {
392                point: Point(vec![2.0, 2.0, 1.0]),
393                distance: 1.0
394            }
395        ];
396
397        let result = multilaterate(measurements).unwrap().0;
398        assert_eq!(result.len(), 3);
399        let delta = 0.1;
400        assert!(is_in_delta(delta, result[0], 2.0));
401        assert!(is_in_delta(delta, result[1], 1.0));
402        assert!(is_in_delta(delta, result[2], 1.0));
403    }
404
405    #[test]
406    fn multilat_fails_on_different_dimension() {
407        let measurements = vec![
408            Measurement {
409                point: Point(vec![1.0, 1.0 ]),
410                distance: 1.0
411            },
412            Measurement {
413                point: Point(vec![3.0, 1.0, 1.0]),
414                distance: 1.0
415            },
416            Measurement {
417                point: Point(vec![2.0, 1.0]),
418                distance: 1.0
419            }
420        ];
421
422        let result = multilaterate(measurements);
423        assert!(result.is_err());
424    }
425
426    #[test]
427    fn multilat_fails_on_zero_dimensions() {
428        let measurements = vec![
429            Measurement {
430                point: Point(vec![]),
431                distance: 1.0
432            },
433            Measurement {
434                point: Point(vec![]),
435                distance: 1.0
436            },
437            Measurement {
438                point: Point(vec![]),
439                distance: 1.0
440            }
441        ];
442
443        let result = multilaterate(measurements);
444        assert!(result.is_err());
445    }
446}