sr_rcd/lib.rs
1/*!
2# SR-RCD
3
4Apply Refining-Cover-by-Defect algorithm to solve Sound Ranging problem in time-dependent-metric (and, in particular, (quasi-)metric) spaces.
5
6Part of "*Decaennead*: ruminations about sound ranging in abstract space(time)s and related themes" project.
7
8## Sound ranging
9
10There is an unknown point **s**, *source*, which at unknown instant t<sub>0</sub> of time emits the sound. The sound wave propagates through space and reaches known points **r**<sub>i</sub>, *sensors*, at known instants t<sub>i</sub>. The *sound ranging* (SR) problem, also called *source localization* or *time-difference-of-arrival* (TDOA) problem, is to determine **s** and t<sub>0</sub> from {**r**<sub>i</sub>, t<sub>i</sub>}.
11
12Supposedly the earliest records of a SR problem date from the World War I period; inter alia, in 1915, it was briefly mentioned in the correspondence between E. Borel and H. Lebesgue.
13
14By replacing sound with electromagnetic waves or some kind of "propagation", e.g. in a graph, one moves from acoustical to optical and other contexts.
15
16## Timetrics
17
18**F-timetric.** Let X be a non-empty set and τ be a nonnegative function defined on ℝ×X×X that satisfies the following axioms:
19
201. *Forward reflexivity:* for any t ∊ ℝ: τ<sub>t</sub>(**x**, **y**) = 0 if and only if **x** = **y**,
21
222. *Forward timed triangle inequality:* for any t ∊ ℝ and any **x**, **y**, **z** ∊ X: τ<sub>t</sub>(**x**, **y**) ≤ τ<sub>t</sub>(**x**, **z**) + τ<sub>t + τ<sub>t</sub>(**x**, **z**)</sub>(**z**, **y**).
23
24Then (X, τ) is called an *f-timetric* space.
25
26In SR context, τ<sub>t</sub>(**x**, **y**) is interpreted as the time needed for the sound wave that was emitted in **x** at the instant t to propagate from **x** to **y**. This wave reaches **y** at the instant t + τ<sub>t</sub>(**x**, **y**).
27
28<hr>
29
30**B-timetric.** Let X be a non-empty set and τ̅ be a nonnegative function defined on ℝ×X×X that satisfies
31
321. *Backward reflexivity:* for any t ∊ ℝ: τ̅<sub>t</sub>(**x**, **y**) = 0 if and only if **x** = **y**,
33
342. *Backward timed triangle inequality:* for any t ∊ ℝ and any **x**, **y**, **z** ∊ X: τ̅<sub>t</sub>(**x**, **y**) ≤ τ̅<sub>t</sub>(**z**, **y**) + τ̅<sub>t - τ̅<sub>t</sub>(**z**, **y**)</sub>(**x**, **z**).
35
36Then (X, τ̅) is called a *b-timetric* space.
37
38In SR context, τ̅<sub>t</sub>(**x**, **y**) is interpreted as the time needed for the sound wave that was emitted in **x** and reached **y** at the instant t to propagate from **x** to **y**.
39This wave was emitted in **x** at the instant t - τ̅<sub>t</sub>(**x**, **y**).
40
41<hr>
42
43Timed triangle inequalities represent "a wave propagates through a medium as soon as possible" notion.
44Note that there is τ<sub>t + τ<sub>t</sub>(**x**, **z**)</sub>(**z**, **y**), not just τ<sub>t</sub>(**z**, **y**), in e.g. forward TTI, because a medium changes and waves propagate through it simultaneously.
45
46<hr>
47
48The connection between f- and b-timetrics:
49
50τ̅<sub>t</sub>(**x**, **y**) = τ<sub>t - τ̅<sub>t</sub>(**x**, **y**)</sub>(**x**, **y**)
51
52τ<sub>t</sub>(**x**, **y**) = τ̅<sub>t + τ<sub>t</sub>(**x**, **y**)</sub>(**x**, **y**)
53
54<hr>
55
56If τ does not depend on t, then we have a *quasi-metric*:
57
581. *Reflexivity:* τ(**x**, **y**) = 0 if and only if **x** = **y**,
59
602. *Oriented triangle inequality:* for any **x**, **y**, **z** ∊ X: τ(**x**, **y**) ≤ τ(**x**, **z**) + τ(**z**, **y**).
61
62If a quasi-metric τ satisfies
63
643. *Symmetry:* for any **x**, **y** ∊ X: τ(**x**, **y**) = τ(**y**, **x**)
65
66then (X, τ) is a usual *metric* space.
67
68For a fixed t, in general case, a timetric τ<sub>t</sub>(·) does not have to be a metric or even a quasi-metric.
69
70## Quick start
71
72```ignore
73extern crate sr_rcd;
74
75use std::time::Instant;
76
77use sr_rcd::{
78 Point,
79 TimetricSpace,
80 rcd,
81 RMPAtmo,
82 Sensor
83};
84
85fn main() {
86 let space = RMPAtmo::new(3, 3.14, &Point::new(vec![3.4, -5.6, 7.8]), 0.03, 1e-4);
87
88 let sensorium = vec![
89 Sensor::make(&Point::new(vec![19.894, -437.750, -455.413]), -244.940),
90 Sensor::make(&Point::new(vec![216.980, -481.563, -45.759]), -153.382),
91 Sensor::make(&Point::new(vec![85.351, -196.650, -422.263]), -300.745),
92 Sensor::make(&Point::new(vec![61.788, 287.007, 384.448]), 383.750),
93 Sensor::make(&Point::new(vec![-467.058, -97.973, -241.471]), 246.294),
94 Sensor::make(&Point::new(vec![-234.397, -150.994, -429.143]), 10.128),
95 Sensor::make(&Point::new(vec![-86.850, -215.597, 248.349]), 175.276),
96 Sensor::make(&Point::new(vec![50.284, 460.754, -16.951]), 315.329),
97 Sensor::make(&Point::new(vec![-294.371, -61.068, -427.287]), 82.082),
98 Sensor::make(&Point::new(vec![475.401, 353.986, 62.538]), 242.594),
99 Sensor::make(&Point::new(vec![263.802, -321.952, -408.792]), -480.919),
100 Sensor::make(&Point::new(vec![-112.307, 493.069, -168.420]), 343.031)
101 ];
102
103 let start = Instant::now();
104 match rcd(&space, &sensorium, &Point::new_default(space.dim()), 1000.0, 0.1, 0.001, false) {
105 Ok(z) => {
106 println!("Time: {:.3} sec", start.elapsed().as_secs_f64());
107 println!("RCD-approximated source: {:.4?}", &z);
108 },
109 Err(s) => println!("ERROR: {}", s)
110 }
111}
112```
113
114The execution result looks like
115
116```text
117Iteration 1: 75 coverands, d = 3342.0306
118Iteration 2: 428 coverands, d = 4013.1741
119Iteration 3: 501 coverands, d = 2568.8301
120Iteration 4: 87 coverands, d = 848.0291
121Iteration 5: 59 coverands, d = 357.5475
122Iteration 6: 44 coverands, d = 178.7737
123Iteration 7: 20 coverands, d = 40.2004
124Iteration 8: 13 coverands, d = 20.1002
125Iteration 9: 15 coverands, d = 10.0501
126Iteration 10: 15 coverands, d = 3.8286
127Iteration 11: 17 coverands, d = 2.1544
128Iteration 12: 16 coverands, d = 0.9355
129Iteration 13: 17 coverands, d = 0.4677
130Iteration 14: 16 coverands, d = 0.2339
131Iteration 15: 17 coverands, d = 0.1365
132Iteration 16: 15 coverands, d = 0.0585
133Time: 4.348 sec
134RCD-approximated source: [262.0173, -319.4046, -392.4598]
135```
136
137`Time` here was obtained with `release` profile; `dev` one is nearly 5 times slower.
138
139See `bin/random.rs` and `bin/manual.rs`.
140
141## RCD
142
143The algorithm is considered in [PDF preprint](https://www.researchgate.net/publication/355344503_On_sound_ranging_in_time-dependent-metric_spaces); see also preceding [another PDF preprint](https://www.researchgate.net/publication/354236739_On_sound_ranging_in_quasi-metric_spaces) and [doi:10.12697/ACUTM.2020.24.14](https://doi.org/10.12697/ACUTM.2020.24.14) for simpler versions regarding quasi-metric and proper metric spaces respectively. (Or maybe this method was conceived long ago, in some paper from 1920–50?..) Basically, part of space is covered by balls, then the cover is iteratively refined by replacing each ball with its cover by balls of halved radius and discarding the ones such that certain "spread of pasts" at their centers is larger than their doubled radius, until the cover becomes small enough. It is a particular case of the so-called "exclude & enclose" approach.
144
145Its implementation is in `rcd.rs`.
146
147## Custom spaces
148
149In principle, the algorithm works in any b-timetric space under some assumptions, here there is only a normed space ℝ<sup>m</sup><sub>p</sub> with periodically shifting atmosphere or constant wind, — see `rmp.rs`. As soon as `TimetricSpace` trait is implemented likewise for `AnotherSpace`, RCD can work with SR problem in `AnotherSpace`. Also, RCD itself does not require `ftau()` method of that trait, hence, if SR data is already present, this method can be stubbed.
150
151## Robustness
152
153To simulate noise in measurements, add N<sub>0,σ<sup>2</sup></sub>-distributed error to each t<sub>i</sub> by setting `err_dev` parameter of `make_random_sr_problem()` to σ. When other parameters take default values, in particular, the approximation precision δ = 0.1, for σ = δ/10 the algorithm fails in nearly 30% of runs as the cover becomes empty, and for σ = δ/5 the failure rate is nearly 90%.
154
155## Higher dimensions and stronger winds
156
157In 4D (moreover 5D etc.) or with angular frequency of atmosphere shift increased from 0.03 to 0.07, the process almost always stucks for a long, on a general purpose computer of nowadays, time at 2nd iteration, with the number of coverands after 1st iteration in hundreds. Extensively, this can be circumvented by using a fast enough computer, but there may be other optimizations.
158
159So, for higher dimensions or stronger winds this is mostly in theoretical realm for now...
160*/
161
162mod point;
163mod tmspace;
164mod rcd;
165mod rmp;
166
167pub use point::Point;
168pub use tmspace::TimetricSpace;
169pub use rcd::{Sensor, rcd};
170pub use rmp::{RMPAtmo, RMPWind};
171
172