# SR-RCD
Apply Refining-Cover-by-Defect algorithm to solve Sound Ranging problem in time-dependent-metric (and, in particular, (quasi-)metric) spaces.
Part of "*Decaennead*: ruminations about sound ranging in abstract space(time)s and related themes" project.
## Sound ranging
There is an unknown point **s**, *source*, which at unknown instant t0 of time emits the sound. The sound wave propagates through space and reaches known points **r**i, *sensors*, at known instants ti. The *sound ranging* (SR) problem, also called *source localization* or *time-difference-of-arrival* (TDOA) problem, is to determine **s** and t0 from {**r**i, ti}.
Supposedly 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.
By replacing sound with electromagnetic waves or some kind of "propagation", e.g. in a graph, one moves from acoustical to optical and other contexts.
## Timetrics
**F-timetric.** Let X be a non-empty set and τ be a nonnegative function defined on ℝ×X×X that satisfies the following axioms:
1. *Forward reflexivity:* for any t ∊ ℝ: τt(**x**, **y**) = 0 if and only if **x** = **y**,
2. *Forward timed triangle inequality:* for any t ∊ ℝ and any **x**, **y**, **z** ∊ X: τt(**x**, **y**) ≤ τt(**x**, **z**) + τt + τt(**x**, **z**)(**z**, **y**).
Then (X, τ) is called an *f-timetric* space.
In SR context, τt(**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 + τt(**x**, **y**).
**B-timetric.** Let X be a non-empty set and τ̅ be a nonnegative function defined on ℝ×X×X that satisfies
1. *Backward reflexivity:* for any t ∊ ℝ: τ̅t(**x**, **y**) = 0 if and only if **x** = **y**,
2. *Backward timed triangle inequality:* for any t ∊ ℝ and any **x**, **y**, **z** ∊ X: τ̅t(**x**, **y**) ≤ τ̅t(**z**, **y**) + τ̅t - τ̅t(**z**, **y**)(**x**, **z**).
Then (X, τ̅) is called a *b-timetric* space.
In SR context, τ̅t(**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**.
This wave was emitted in **x** at the instant t - τ̅t(**x**, **y**).
Timed triangle inequalities represent "a wave propagates through a medium as soon as possible" notion.
Note that there is τt + τt(**x**, **z**)(**z**, **y**), not just τt(**z**, **y**), in e.g. forward TTI, because a medium changes and waves propagate through it simultaneously.
The connection between f- and b-timetrics:
τ̅t(**x**, **y**) = τt - τ̅t(**x**, **y**)(**x**, **y**)
τt(**x**, **y**) = τ̅t + τt(**x**, **y**)(**x**, **y**)
If τ does not depend on t, then we have a *quasi-metric*:
1. *Reflexivity:* τ(**x**, **y**) = 0 if and only if **x** = **y**,
2. *Oriented triangle inequality:* for any **x**, **y**, **z** ∊ X: τ(**x**, **y**) ≤ τ(**x**, **z**) + τ(**z**, **y**).
If a quasi-metric τ satisfies
3. *Symmetry:* for any **x**, **y** ∊ X: τ(**x**, **y**) = τ(**y**, **x**)
then (X, τ) is a usual *metric* space.
For a fixed t, in general case, a timetric τt(·) does not have to be a metric or even a quasi-metric.
## Quick start
```rust
extern crate sr_rcd;
use std::time::Instant;
use sr_rcd::{
Point,
TimetricSpace,
rcd,
RMPAtmo,
Sensor
};
fn main() {
let space = RMPAtmo::new(3, 3.14, &Point::new(vec![3.4, -5.6, 7.8]), 0.03, 1e-4);
let sensorium = vec![
Sensor::make(&Point::new(vec![19.894, -437.750, -455.413]), -244.940),
Sensor::make(&Point::new(vec![216.980, -481.563, -45.759]), -153.382),
Sensor::make(&Point::new(vec![85.351, -196.650, -422.263]), -300.745),
Sensor::make(&Point::new(vec![61.788, 287.007, 384.448]), 383.750),
Sensor::make(&Point::new(vec![-467.058, -97.973, -241.471]), 246.294),
Sensor::make(&Point::new(vec![-234.397, -150.994, -429.143]), 10.128),
Sensor::make(&Point::new(vec![-86.850, -215.597, 248.349]), 175.276),
Sensor::make(&Point::new(vec![50.284, 460.754, -16.951]), 315.329),
Sensor::make(&Point::new(vec![-294.371, -61.068, -427.287]), 82.082),
Sensor::make(&Point::new(vec![475.401, 353.986, 62.538]), 242.594),
Sensor::make(&Point::new(vec![263.802, -321.952, -408.792]), -480.919),
Sensor::make(&Point::new(vec![-112.307, 493.069, -168.420]), 343.031)
];
let start = Instant::now();
match rcd(&space, &sensorium, &Point::new_default(space.dim()), 1000.0, 0.1, 0.001, false) {
Ok(z) => {
println!("Time: {:.3} sec", start.elapsed().as_secs_f64());
println!("RCD-approximated source: {:.4?}", &z);
},
Err(s) => println!("ERROR: {}", s)
}
}
```
The execution result looks like
```text
Iteration 1: 75 coverands, d = 3342.0306
Iteration 2: 428 coverands, d = 4013.1741
Iteration 3: 501 coverands, d = 2568.8301
Iteration 4: 87 coverands, d = 848.0291
Iteration 5: 59 coverands, d = 357.5475
Iteration 6: 44 coverands, d = 178.7737
Iteration 7: 20 coverands, d = 40.2004
Iteration 8: 13 coverands, d = 20.1002
Iteration 9: 15 coverands, d = 10.0501
Iteration 10: 15 coverands, d = 3.8286
Iteration 11: 17 coverands, d = 2.1544
Iteration 12: 16 coverands, d = 0.9355
Iteration 13: 17 coverands, d = 0.4677
Iteration 14: 16 coverands, d = 0.2339
Iteration 15: 17 coverands, d = 0.1365
Iteration 16: 15 coverands, d = 0.0585
Time: 4.348 sec
RCD-approximated source: [262.0173, -319.4046, -392.4598]
```
`Time` here was obtained with `release` profile; `dev` one is nearly 5 times slower.
See `bin/random.rs` and `bin/manual.rs`.
## RCD
The 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.
Its implementation is in `rcd.rs`.
## Custom spaces
In principle, the algorithm works in any b-timetric space under some assumptions, here there is only a normed space ℝmp 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.
## Robustness
To simulate noise in measurements, add N0,σ2-distributed error to each ti 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%.
## Higher dimensions and stronger winds
In 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.
So, for higher dimensions or stronger winds this is mostly in theoretical realm for now...