Skip to content

ServiceRadar

Open navigation

Engineering blog

August 18, 2026 Michael Freeman

FieldSurvey

How FieldSurvey splits LiDAR pose on the iPhone from monitor-mode Wi-Fi (and optional HackRF spectrum) on a Raspberry Pi Sidekick, then live-streams Arrow batches into ServiceRadar. Hardware and build notes; TestFlight soon.

fieldsurvey ios rust raspberry-pi wifi lidar arkit

FieldSurvey is a walk-through Wi-Fi survey that writes into ServiceRadar. The iPhone runs RoomPlan and ARKit for the floorplan and pose, and a Raspberry Pi Sidekick collects RF from USB adapters in Linux monitor mode (beacons and probe responses) plus optional HackRF One channel-energy sweeps, because iOS will not put a third-party app's Wi-Fi radio in monitor mode. If you are signed in, the phone opens Arrow WebSockets to web-ng and streams RF, pose, and spectrum batches as you walk, which you can follow on /spatial/field-surveys ; it also checkpoints a local session so a dropped cell link does not lose the walk, and you can resume the stream or upload the bundle later.

Hardware

Raspberry Pi 4, Anker 140W power bank, HackRF One, and USB Wi-Fi adapters as FieldSurvey Sidekick internals
Pi 4, Anker 24,000 mAh 140W, HackRF One, and USB radios. Keep onboard wlan0 on the phone link and use USB only for capture.

A 3D-printed clamshell for the Pi, power bank, and radios is in progress, so the photo is the current internals. You need a LiDAR iPhone Pro (12 Pro or later, iOS 16+) because RoomPlan uses the depth camera, and the Sidekick is a Raspberry Pi 4 with 4 GB on 64-bit Raspberry Pi OS (a Pi 5 works) powered from an Anker 24,000 mAh 140W bank over USB-C, which is more than the Pi needs and will also charge the phone. Capture adapters have to expose monitor mode and radiotap through iw ; the ones we have verified are Ralink RT5572 ( rt2800usb ) and MediaTek MT7612U ( mt76x2u ), including Alfa's AWUS036ACM. Prefer USB 3 for the 5 GHz radio. The usual setup is two adapters, one parked on 2.4 GHz 1/6/11 and one hopping 5 GHz, plus an optional HackRF One that we treat as receive-only unless we know that unit's modification history; Sidekick starts hackrf_sweep with the RX amp and antenna power off. Two dongles plus a HackRF usually wants a powered hub, a 32 GB or larger microSD, and a USB-C cable.

The phone and Pi talk over IP, using iPhone Personal Hotspot over USB in the field or ethernet / the same LAN on a bench. The daemon listens on 17321 for HTTP control and the observation WebSockets. Pairing is a one-time claim against SERVICERADAR_SIDEKICK_API_TOKEN that returns a device token the daemon stores only as a hash and the app keeps in the iOS Keychain, so replace the change-me default in /etc/serviceradar/fieldsurvey-sidekick.env before you take the kit out.

Data path

Pairing, status, and radio-plan updates are JSON, but RF is Apache Arrow IPC on one WebSocket per monitor radio, with spectrum on a separate Arrow WebSocket so BSSID rows and power bins stay distinct. The phone downsamples onto the current ARKit pose for the on-device map and forwards the same Arrow bytes to ServiceRadar on three WebSockets (RF, pose, spectrum), and web-ng writes those batches into Postgres through ADBC as they arrive: platform.survey_rf_observations , platform.survey_pose_samples , and platform.survey_spectrum_observations . A view joins RF to pose by session and timestamp, so review can follow a session that is still walking. We also persist Wi-Fi RSSI and HackRF interference rasters from the fused rows, store floorplan artifacts in object storage on the session, and can pin a raster on a dashboard card with in:field_survey_rasters overlay_type:wifi_rssi has_floorplan:true sort:generated_at:desc .

Building the Rust daemon

The crate is serviceradar-fieldsurvey-sidekick in rust/fieldsurvey-sidekick , with Axum for HTTP, Linux AF_PACKET / TPACKET_V3 mmap rings for capture, and packaging under build/packaging/fieldsurvey-sidekick/ . Build on the Pi, or cross-compile with aarch64-linux-gnu-gcc as configured in .cargo/config.toml ( gcc-aarch64-linux-gnu on Debian):

git clone https://github.com/carverauto/serviceradar.git
cd serviceradar

# on the Pi:
cargo build -p serviceradar-fieldsurvey-sidekick --release

# from a laptop:
rustup target add aarch64-unknown-linux-gnu
cargo build -p serviceradar-fieldsurvey-sidekick \
  --release --target aarch64-unknown-linux-gnu

Copy the binary and packaging files to the Pi and install them. Replace pi@pi-host with your user and host:

scp target/aarch64-unknown-linux-gnu/release/serviceradar-fieldsurvey-sidekick \
  pi@pi-host:/tmp/serviceradar-fieldsurvey-sidekick.new

scp build/packaging/fieldsurvey-sidekick/config/fieldsurvey-sidekick.toml \
  build/packaging/fieldsurvey-sidekick/config/fieldsurvey-sidekick.env.example \
  build/packaging/fieldsurvey-sidekick/systemd/serviceradar-fieldsurvey-sidekick.service \
  pi@pi-host:/tmp/

ssh pi@pi-host '
  sudo install -m 0755 /tmp/serviceradar-fieldsurvey-sidekick.new \
    /usr/local/bin/serviceradar-fieldsurvey-sidekick
  sudo install -d -m 0755 /etc/serviceradar
  sudo install -m 0644 /tmp/fieldsurvey-sidekick.toml \
    /etc/serviceradar/fieldsurvey-sidekick.toml
  sudo install -m 0600 /tmp/fieldsurvey-sidekick.env.example \
    /etc/serviceradar/fieldsurvey-sidekick.env
  sudo install -m 0644 /tmp/serviceradar-fieldsurvey-sidekick.service \
    /etc/systemd/system/serviceradar-fieldsurvey-sidekick.service
  sudo systemctl daemon-reload
  sudo systemctl enable --now serviceradar-fieldsurvey-sidekick.service
'
curl -s http://pi-host:17321/healthz
curl -s http://pi-host:17321/status
iw dev
iw phy | sed -n "/Supported interface modes:/,/Band /p"

/status should list the USB radios. If monitor setup fails, check that the interface is not the management uplink and that NetworkManager is not associating the capture dongle. The service runs as root with CAP_NET_ADMIN and CAP_NET_RAW because it reconfigures interfaces and opens packet sockets, and you will want a udev rule if USB enumeration order changes between boots. In auto mode the iPhone ignores the management interface, sorts monitor-capable USB radios by link speed, and assigns 5 GHz to the fastest and 2.4 GHz 1/6/11 to the next; an explicit plan looks like wlan1:2412|2437|2462,wlan2:5180|5200|5220|5240 .

Building the iOS app

The project is swift/FieldSurvey/FieldSurvey.xcodeproj , bundle id com.serviceradar.FieldSurvey . Arrow Swift is vendored under LocalPackages/arrow-swift so the Sidekick stream and the ServiceRadar ingest share an encoding. The app needs camera, local network, and motion permissions for RoomPlan and the USB or LAN link.

cd swift/FieldSurvey
open FieldSurvey.xcodeproj

Select a development team, plug in a LiDAR iPhone, and run the FieldSurvey scheme. The simulator can run SidekickClientTests and SignalCoverageInterpolatorTests but cannot do RoomPlan or talk to a Pi.

xcodebuild -project FieldSurvey.xcodeproj \
  -scheme FieldSurvey \
  -destination 'generic/platform=iOS' \
  CODE_SIGNING_ALLOWED=NO \
  build

TestFlight is coming and I will add the public link here when Apple has the build. In Settings, set the Sidekick URL to http://<pi-host>:17321 (or fieldsurvey-rpi.local if you advertise mDNS), paste the setup token, claim the phone, point the backend at your ServiceRadar instance, sign in, and run Check Backend; after that, starting a survey streams to ServiceRadar. Leave RF scanning on. AR priority mode pauses the Sidekick preview when tracking degrades so RoomPlan can recover, then resumes it.

Running a survey

Start a full room scan, hold the phone like a camera, and walk the perimeter and then the interior. The live map should show heat points, AP chips, RF batch counts, and a climbing backend frame count if you are signed in; enable spectrum in Settings if you have a HackRF and want the spectrum bar. Manual AP marks on a closet or ceiling tile are treated as ground truth, inferred positions from RSSI gradients carry a confidence score, and HackRF energy is not used to place a BSSID. Check the 2D review on the phone before you leave. Coming back for an RF-only pass reuses the saved floorplan and requires an alignment step before new heat is written into the old coordinates.

FieldSurvey iOS Live Signal Map with a floorplan overlay, AP chips, RSSI heat points, and a HackRF spectrum bar
Live Signal Map. 12 APs, a couple thousand heat points, HackRF running. The triangle is the current pose.
ServiceRadar dashboard FieldSurvey heatmap card with a floorplan outline and RSSI color scale
Dashboard raster after a streamed walk. Same floorplan and RSSI scale as the phone.

Operator notes are in the FieldSurvey Sidekick guide. Source is rust/fieldsurvey-sidekick and swift/FieldSurvey .