Android wardriving app for Reticulum networks. Records LoRa coverage by transmitting GPS beacons via RNS/LXMF, displays a live signal heatmap overlay, and supports bidirectional probe mode for link quality measurement.
  • Kotlin 99.6%
  • Python 0.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Nickie Deuxyeux 18c4aafa7f permissions: make notifications optional instead of blocking
The location foreground service runs without POST_NOTIFICATIONS; the
permission only controls whether the session notification shows. Only
location now gates the app, and notifications are offered once,
non-blockingly, before the background location offer.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 15:31:27 +03:00
app permissions: make notifications optional instead of blocking 2026-10-09 15:31:27 +03:00
gradle deps: bump MapLibre to 13.6.1 2026-10-07 01:05:16 +03:00
plugins plugins: add the phystats Sideband service plugin 2026-10-06 23:37:14 +03:00
.gitignore chore: ignore .claude/ directory 2026-07-08 16:00:54 +03:00
app-logo.png Initial commit 2026-06-26 20:30:47 +03:00
build.gradle.kts Initial commit 2026-06-26 20:30:47 +03:00
Columba_RPC_Setup.md docs: add Columba RPC key extraction guide, link from probe-mode setup 2026-07-06 17:25:19 +03:00
gradle.properties Initial commit 2026-06-26 20:30:47 +03:00
gradlew Initial commit 2026-06-26 20:30:47 +03:00
gradlew.bat Initial commit 2026-06-26 20:30:47 +03:00
LICENSE docs: add MIT license, clarify shared-instance requirement and probe mode 2026-07-03 16:03:37 +03:00
README.md docs: note minimum RNode_Firmware 1.86.1111 for Firmware responder 2026-10-06 23:46:35 +03:00
settings.gradle.kts Initial commit 2026-06-26 20:30:47 +03:00

RNS Wardrive

An Android wardriving app for Reticulum (RNS). It tracks your GPS position while you drive or walk, measures the radio link in both directions (uplink and downlink RSSI/SNR), and maps the result — either on a collector bot over RNS, or entirely on your phone using an RNode running the wardrive responder firmware.

What it does

  • Runs a persistent foreground service that tracks your GPS position and reaches RNS through a shared instance (Sideband or Columba) on the same device
  • Four operating modes: Announce, Packet, Probe (bot-based) and Firmware responder (no bot, everything stays on the phone) — see Operating Modes
  • Measures uplink (how well the responder/bot heard you) and downlink (how well you heard the reply) RSSI/SNR. Downlink needs the Sideband phystats plugin
  • Live interactive MapLibre map with a local signal heatmap (Uplink, Downlink or Both) — tap a cell to see its numbers
  • Uplink and Downlink signal meters on the status card and in the Log, with per-entry reply status
  • Local Data: import and export your measurements as CSV, GeoJSON or GPX, with no server involved — the exports open directly on a wardrive map
  • Offline maps: download an area and keep mapping with no internet
  • Session controls: Start, Pause/Resume, Stop, "Send now", plus pre-start checks
  • Smart Updates: skip transmissions if you haven't moved more than a configurable distance

Requirements

  • Android 10+ (API 29)
  • An RNS shared instance accessible from the device — provided by an app such as Sideband or Columba running on the same device
  • For downlink (phy) measurements: Sideband plus the phystats plugin. The plugin is still required for any bidirectional measurement — see Downlink (phy stats). Without it the app still works, but samples carry uplink only.
  • For Announce / Packet / Probe: rns-wardrive-tools running server-side — it provides the collector bot that receives your beacons. The app discovers reachable bots from their RNS announces.
  • For Firmware responder: an RNode running RNode_Firmware 1.86.1111 or newer, the minimum version that includes the wardrive responder. No bot is needed.

Download & Install

Download the latest APK from the releases page and install it on your device. Releases are currently debug-signed pre-releases.

Prefer to build it yourself? See Building at the bottom.

Setup

  1. Install and open the app. Grant location permissions when prompted (including "Allow all the time" for background tracking).

  2. Start your RNS shared instance (Sideband or Columba) on the device. The app connects to it on port 37428 by default.

  3. Install the phystats plugin (for downlink). In Sideband, install phystats_plugin.py — details in Downlink (phy stats).

  4. Pick your measurement source.

    • Firmware responder: nothing else to set up beyond an RNode running RNode_Firmware 1.86.1111 or newer.
    • Announce / Packet / Probe: install and run rns-wardrive-tools and note the bot's RNS destination hash (or pick it from the discovered bots).
  5. Open Settings and configure:

    • Operating mode — Announce, Packet, Probe or Firmware responder
    • Bot hash(es) — destination hash of your collector bot (bot-based modes)
    • Probe interval — seconds between probes in Firmware responder mode (default 10, minimum 2)
    • Beacon interval / ping and path timeouts — timings for the bot-based modes
    • Smart updates — minimum distance (meters) you must move before a new beacon or probe is sent
    • Shared instance port / RPC port / RPC Key — for non-default ports and for downlink (see below)
    • Map style and Offline maps
  6. Tap Start Session on the map screen and confirm.

Operating Modes

Mode Description
Announce Broadcasts your position as an RNS announce. Works without a path to the bot. Lowest latency, no delivery confirmation.
Packet Sends a direct encrypted packet to the collector bot. Falls back to Announce if no path is known yet.
Probe Bi-directional. Like Packet, but each beacon carries the downlink RSSI/SNR measured from the bot's previous reply, and the bot echoes back the uplink RSSI/SNR it measured for your packet. Useful for two-way RF link quality surveys. Real downlink values need the phystats plugin and RPC key.
Firmware responder No bot and no server. The app probes an RNode running the wardrive responder (RNode_Firmware 1.86.1111 or newer) once per probe interval (default 10 s, about 3% airtime at SF8). Each reply gives the responder's uplink RSSI/SNR, and — with the phystats plugin — your downlink RSSI/SNR. Every probe, answered or not, is stored on the phone and drawn on the local heatmap; unanswered probes appear as cold spots.

Downlink is the signal quality of the reply as heard by your own device's radio. A shared-instance client such as this app cannot see that directly. Reticulum caches each received packet's RSSI/SNR, and serves it over the instance's authenticated RPC port, but only for interfaces that set reports_phy_stats. Sideband's Android RNode interface never sets it (neither does Columba's), so by default there is nothing to read.

The phystats Sideband service plugin fixes that, and it is still required for bidirectional work (Probe mode and Firmware responder downlink):

  1. Get plugins/phystats_plugin.py from this repository and copy it to a directory on the phone.
  2. In Sideband open Plugins, enable service plugins, choose that directory, and restart Sideband (plugins load at startup).
  3. In Sideband Connectivity tap Copy RPC Config To Clipboard, then paste it into this app's Settings → RPC Key. Leave RPC port at 37429. Use Test RPC Connection to verify.

Nothing in Sideband is patched; disabling the plugin and restarting Sideband puts everything back. Without the plugin or the RPC key, measurements still work and are stored with uplink only.

Columba doesn't expose its RPC key in the UI (it must be derived from its transport identity file and needs root — see Columba RPC key setup), and the plugin is Sideband-only, so downlink is not available through Columba.

Using the app

Session controls

  • Start / Stop Session ask for confirmation. Before starting, the app checks Flight mode, Bluetooth and the RNode; the button turns yellow, and the dialog says what to fix. Turning Flight mode on during a session stops it immediately.
  • Pause / Resume (button above Stop, and a notification action) stops new probes while GPS and the shared-instance connection stay up.
  • Send now forces a probe immediately (also while paused), but still refuses a missing, stale or inaccurate position.

Status card and Log

  • The status card shows the shared-instance state, counters, the last reply (responder and round-trip time) and Uplink and Downlink RSSI/SNR bars. Captions are coloured like the RNode LEDs: TX blue, RX green. The downlink reading is held until the next one arrives.
  • The Log lists each probe with coordinates, signal bars, and the reply (or a red No reply), and has a draggable scrollbar.

Heatmap

  • Overlay choices are Uplink, Downlink or Both (uplink heatmap plus downlink dots). Cells are about 10 m, coloured by mean RSSI.
  • Tap a cell for its uplink/downlink RSSI/SNR.

Local Data

The Local Data menu on the map screen offers:

  • Import CSV, GeoJSON or GPX; duplicates are skipped, the overlay turns on and the map flies to the imported area
  • Export as GeoJSON, CSV or GPX (unanswered probes are included as replied = false)
  • Clear Local Data (greyed out when there is nothing to clear)

Viewing exports on the wardrive map

Exports do not need a bot or any server-side collector. The map from rns-wardrive-tools can show them directly: open the map page, press Open file (or drop one or more files on the page) and pick the GeoJSON, CSV or GPX file you exported. The file is read in your browser and nothing is uploaded.

  • Answered probes appear on the Uplink heatmap, and on the Downlink heatmap when the sample has downlink values
  • Unanswered probes (replied = false) appear as red dots: the coverage holes
  • Close file returns the map to its live data. A map without a bot database works as a pure file viewer

Maps

  • Map style: OpenFreeMap Liberty, rns.moscow Light / Dark (follow the app theme) / Green / Voyager / OSM, or a Custom MapLibre style URL.
  • Offline maps (Settings → Maps): choose a centre and radius, a zoom range and see the exact tile count and size before downloading. Downloads can be paused, resumed and deleted, and the space used is shown. When there is no internet, the saved style is used and the status card shows an "Offline map" row.

Permissions

Permission Reason
ACCESS_FINE_LOCATION / ACCESS_COARSE_LOCATION GPS position
ACCESS_BACKGROUND_LOCATION Continue tracking when the screen is off
FOREGROUND_SERVICE / FOREGROUND_SERVICE_LOCATION Persistent service
INTERNET / ACCESS_NETWORK_STATE RNS TCP transport, map tiles, offline detection
POST_NOTIFICATIONS Foreground service notification
WAKE_LOCK Prevent CPU sleep during active tracking

Architecture

ui/
  map/          MapScreen + MapViewModel   — live map, heatmap, status card, session controls
  log/          LogScreen + LogViewModel   — measurement log
  bots/         discovered collector bots
  offline/      offline map download and management
  settings/     SettingsScreen + SettingsViewModel, map style picker
  permissions/  permission gate shown before the app starts
plugins/        phystats Sideband service plugin (installed on the phone, not part of the APK)
rns/
  WardriveRnsManager        — RNS lifecycle, beacon sending, probe/ping logic
  FirmwareResponderManager  — probes the RNode firmware responder
  WardriveLxmfManager       — LXMF messaging (bot responses)
  SharedInstanceRpcClient   — RPC client for downlink lookup (MiniMsgPack, AuthMath)
probe/          ProbeEngine, protocol and responder session
heatmap/        local heatmap aggregation and tap hit-testing
export/         GeoJSON / CSV / GPX export and import
offline/        offline map regions, tiles and local style server
session/        pre-start checks (Flight mode, Bluetooth, RNode)
location/       GPS / LocationManager tracking
service/
  WardriveForegroundService — location loop, wakelock, notification
data/
  SettingsRepository  — DataStore-backed settings persistence
  local/              — Room database of wardrive samples
  model/              — WardriveSettings, LocationPoint, LogEntry

The app uses Hilt for DI, Jetpack Compose + Material3 for UI, Room for local samples, and Kotlin coroutines/Flow throughout.

Building

Prerequisites

  • Android Studio Meerkat or later
  • JDK 17
  • Gradle 8.x (managed by the wrapper — no separate install needed)

The app depends on reticulum-kt and LXMF-kt, published to JitPack. No additional setup is needed — Gradle resolves them automatically.

Clone and build

git clone https://git.rns.moscow/deuxyeux/rns-wardrive-app.git
cd rns-wardrive-app
./gradlew assembleDebug

The APK will be at app/build/outputs/apk/debug/app-debug.apk.

For a release build:

./gradlew assembleRelease

You will need to configure a signing keystore in app/build.gradle.kts or via Android Studio before a release build will install on a device.

Install via ADB

adb install app/build/outputs/apk/debug/app-debug.apk

License

Released under the MIT License. See LICENSE.