| Crates.io | locate-rs |
| lib.rs | locate-rs |
| version | 1.0.1 |
| created_at | 2025-07-17 12:14:52.166755+00 |
| updated_at | 2025-08-28 17:53:17.749173+00 |
| description | A `no_std` Rust library for 3D localization using TDOA and Trilateration. It provides fast iterative solvers (Levenberg-Marquardt) and a slower closed-form, eigenvector-based solution (trilateration only) for high-accuracy positioning. |
| homepage | |
| repository | https://github.com/trembel/locate-rs |
| max_upload_size | |
| id | 1757393 |
| size | 87,668 |
A no_std Rust library for 3D localization using TDOA and Trilateration. It provides fast iterative solvers (Levenberg-Marquardt) and a slower closed-form, eigenvector-based solution (trilateration only) for high-accuracy positioning.
This library is designed for resource-constrained environments where alloc is not available.
If you use locate-rs in an academic or industrial context, please cite the following publication:
@misc{cortesi25_wakeloc,
title = {WakeLoc: An Ultra-Low Power, Accurate and Scalable On-Demand RTLS using Wake-Up Radios},
author = {Silvano Cortesi and Christian Vogt and Michele Magno},
year = 2025,
doi = {10.48550/arXiv.2504.20545},
url = {https://doi.org/10.48550/arXiv.2504.20545},
eprint = {2504.20545},
archiveprefix = {arXiv},
primaryclass = {cs.NI}
}
no_std compatible: Suitable for embedded systems and other environments without a standard library.Add locate-rs to your Cargo.toml:
[dependencies]
locate-rs = "1.0"
nalgebra = { version = "0.33.2", default-features = false, features=["libm"] }
heapless = "0.8.0"
Here's a basic example of how to use LocationSolver for trilateration:
use locate_rs::{LocationSolver, LocationError};
use nalgebra::Vector3;
use heapless::FnvIndexMap;
use rand::rngs::SmallRng;
use rand::SeedableRng;
fn main() -> Result<(), LocationError> {
// 1. Define anchor positions
let mut known_locations = FnvIndexMap::<&str, Vector3<f64>, 4>::new();
known_locations.insert("A", Vector3::new(0.0, 0.0, 0.0)).unwrap();
known_locations.insert("B", Vector3::new(10.0, 0.0, 0.0)).unwrap();
known_locations.insert("C", Vector3::new(0.0, 10.0, 10.0)).unwrap();
known_locations.insert("D", Vector3::new(0.0, 0.0, 10.0)).unwrap();
// 2. Ground truth position
let x_gt = Vector3::new(3.4, 4.5, 6.7);
// 3. Create distance map
let mut trilateration_infos = FnvIndexMap::<&str, f64, 4>::new();
trilateration_infos.insert("A", 8.758).unwrap();
trilateration_infos.insert("B", 10.426).unwrap();
trilateration_infos.insert("C", 7.259).unwrap();
trilateration_infos.insert("D", 6.535).unwrap();
// 4. Create solver and solve
let mut solver = LocationSolver::new(&known_locations, 1e-6);
// using iterative solver
let location_fast = solver.trilateration_fast(trilateration_infos.clone(), None)?; // No initial guess of position
// using eigenvalue optimal solver
let mut rng = SmallRng::from_seed([0; 32]);
let location_eigen = solver.trilateration(trilateration_infos, &mut rng)?;
println!("Trilateration (fast) solution: {:?}\r\nTrilateration (eigenvalue) solution: {:?}\r\nGroundtruth: {:?}", location_fast, location_eigen, x_gt);
Ok(())
}
For TDoA, you would provide distance differences instead:
use locate_rs::{LocationSolver, LocationError};
use nalgebra::Vector3;
use heapless::FnvIndexMap;
fn main() -> Result<(), LocationError> {
// 1. Define anchor positions
let mut known_locations = FnvIndexMap::<&str, Vector3<f64>, 4>::new();
known_locations.insert("A", Vector3::new(0.0, 0.0, 0.0)).unwrap();
known_locations.insert("B", Vector3::new(10.0, 0.0, 0.0)).unwrap();
known_locations.insert("C", Vector3::new(0.0, 10.0, 10.0)).unwrap();
known_locations.insert("D", Vector3::new(0.0, 0.0, 10.0)).unwrap();
// 2. Ground truth position
let x_gt = Vector3::new(3.4, 4.5, 6.7);
// 3. Create TDoAs relative to anchor "A"
let mut tdoa_infos = FnvIndexMap::<(&str, &str), f64, 4>::new();
tdoa_infos.insert(("B", "A"), 1.668).unwrap(); // d_B - d_A
tdoa_infos.insert(("C", "A"), -1.498).unwrap(); // d_C - d_A
tdoa_infos.insert(("D", "A"), -2.223).unwrap(); // d_D - d_A
// 4. Create solver and solve
let mut solver = LocationSolver::new(&known_locations, 1e-6);
let location_tdoa = solver.tdoa(tdoa_infos, None)?; // No initial guess of position
println!("TDoA solution: {:?},\r\nGroundtruth: {:?}", location_tdoa, x_gt);
Ok(())
}
To run the tests for this library, use the following command:
cargo test
The repository includes benchmarks using criterion. To run them:
cargo bench
This project is licensed under the LGPL-3.0 license. See the LICENSE file for details.