A signal source localization system using cluster-based Angle-of-Arrival (AoA) triangulation algorithms. The project consists of a C++ triangulation engine, a REST API server, and an Android companion app for data collection.
Project Polaris estimates the location of a signal source (e.g., a radio transmitter) from a set of GPS-tagged signal strength measurements. The core algorithm partitions measurements into spatial clusters, estimates the angle of arrival (AoA) for each cluster using gradient-based methods, and then optimizes a global cost function to find the most likely source location.
- Cluster-based triangulation algorithms (CTA1 and CTA2)
- REST API server for remote signal processing
- Android app (Polaris) for collecting GPS-tagged signal measurements
- Visualization of results via Python plotting scripts
- Comprehensive test suite with unit and integration tests
├── src/ # C++ source code
│ ├── core/ # Core algorithm implementation
│ │ ├── ClusteredTriangulationAlgorithm1.cpp/h
│ │ ├── ClusteredTriangulationAlgorithm2.cpp/h
│ │ ├── Cluster.cpp/h
│ │ ├── DataPoint.cpp/h
│ │ └── JsonSignalParser.cpp/h
│ ├── rest/ # REST API server
│ ├── main.cpp # CLI application entry point
│ └── main_rest_api.cpp # REST server entry point
├── Polaris/ # Android companion app (Kotlin)
├── plotting/ # Python visualization scripts
├── tests/ # Unit and integration tests
├── Recordings/ # Sample signal recording files (JSON)
└── scripts/ # Utility scripts
- CMake 3.14+
- C++17 compatible compiler (GCC, Clang, MSVC)
- OpenMP (optional, for parallel processing)
The following dependencies are automatically fetched via CMake's FetchContent:
- nlohmann/json v3.11.2
- spdlog v1.14.1
- cpp-httplib v0.14.3
- GoogleTest (for testing)
- Android Studio
- Gradle
- Android SDK
- Python 3.x
- matplotlib
- numpy
- pandas
# Build release version
make
# Build debug version
make debug
# Build with profiling support
make profiling
# Clean build directory
make clean
# Full rebuild
make rebuildmkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build . -j$(nproc)./build/signal-triangulation [options] <signals_file.json>| Option | Description |
|---|---|
-h, --help |
Show help message |
--param-help |
Show algorithm parameter help |
-a, --algorithm <name> |
Algorithm to use: CTA1 or CTA2 (default: CTA2) |
-p, --plot |
Enable plotting output |
--log-level <level> |
Set log level (trace, debug, info, warn, error) |
# Run triangulation on a recording file
./build/signal-triangulation Recordings/HalfMoon1.json
# Run with plotting enabled
./build/signal-triangulation -p Recordings/FootballField2.json
# Use CTA1 algorithm
./build/signal-triangulation -a CTA1 Recordings/MergedUrban.jsonPipe the output to the plotting script for visualization:
./build/signal-triangulation -p Recordings/HalfMoon1.json | python3 plotting/plot_from_stdin.py./build/rest-api-serverThe server accepts signal data via HTTP and returns triangulation results.
# Run all tests
make test
# Run unit tests only
make test-unit
# Run integration tests only
make test-integration
# Run a specific test
make test-one TEST=test_triangulationThe Polaris Android app collects GPS-tagged signal strength measurements that can be processed by the triangulation engine.
cd Polaris
./gradlew assembleDebug# Install ADB if needed
make install-adb
# Transfer recordings from connected Android device
make fetch_recordingsThe system implements cluster-based Angle-of-Arrival (AoA) triangulation:
- Clustering: Partition GPS-tagged signal measurements into spatial clusters
- AoA Estimation: Fit a local plane to each cluster's signal strength field; the gradient indicates the direction toward the source
- Vector Creation: Convert each cluster into a weighted direction vector
- Optimization: Minimize a cost function based on perpendicular distances from candidate locations to cluster rays
- Outlier Rejection: Identify and exclude anomalous clusters for robustness
Signal data is provided in JSON format. The following is an example of a single measurement point:
{
"measurements": [
{
"deviceID": "cb32e7f6ba0ea81a", //unique identifier for the recording device
"id": 101,
"latitude": 59.86614995555554,
"longitude": 17.70517585555555,
"rssi": -73,
"ssid": "wifi-hotspot",
"timestamp": 1764845637110 //timestamp in UNIX time format
}
]
}Sample recordings are provided in the Recordings/ and oldRecordings/ directories.