Skip to content

feat: add true geographic bearing for Remote ID drones, Meshtastic/Samsung decoders, and RF anomaly detection - #15

Closed
edwin042331-hue wants to merge 2 commits into
OffGridPete:mainfrom
edwin042331-hue:feature/tactical-bearing-and-rf-defense
Closed

edwin042331-hue wants to merge 2 commits into
OffGridPete:mainfrom
edwin042331-hue:feature/tactical-bearing-and-rf-defense

Conversation

@edwin042331-hue

Copy link
Copy Markdown

Overview

This pull request introduces tactical RF situational awareness and defense capabilities to Fieldwatch:

  1. True Geographic Bearing (Azimuth) on Classic Radar for Drones:

    • Calculates great-circle spherical initial bearing (RadarPlot.initialBearing) for aircraft transmitting ASTM F3411 Remote ID (payloadLat/payloadLon) relative to the operator's GPS fix.
    • Replaces random MAC hash angles with true physical bearing relative to North.
    • Contacts with calibrated true bearing are rendered with a tactical diamond marker.
    • Added cardinal compass markers (N, S, E, W) on the radar disc perimeter.
  2. Native Meshtastic BLE Service Decoder (0xCBF0):

    • Added automatic detection for service UUID 0xCBF0 in AdvPayloadDecoder.kt.
    • Correctly identifies and categorizes Meshtastic LoRa nodes into the MESH class even when custom callsigns or alias names are used.
  3. Samsung SmartThings Find / Galaxy SmartTag Telemetry Decoder (0x0075):

    • Added manufacturer payload decoder for Samsung SmartTag, SmartTag2, and SmartThings Find offline beacons.
  4. RF Anomaly & BLE Spam Defense Engine (RfAnomalyDetector.kt):

    • Analyzes bursts of ephemeral randomized MAC advertisements carrying pairing payloads (Apple Continuity 0x07, FastPair 0xFE2C, SwiftPair 0x0006).
    • Flags active BLE Spam floods (typical of Flipper Zero, ESP32 Marauder, or Bruce firmware) with a real-time warning indicator (⚠️ BLE FLOOD) in the header row.

Testing & Validation

  • Unit Tests: Added full test suites in RadarPlotTest.kt, AdvPayloadDecoderTest.kt, and RfAnomalyDetectorTest.kt.
  • Result: 389/389 unit tests passing cleanly (./gradlew testDebugUnitTest).
  • Live Device: Built and validated on a physical Android 13 device.

Copy link
Copy Markdown
Owner

Thank you for putting this together. I will update once I have an opportunity to fully review it.

Copy link
Copy Markdown
Owner

Thank you for the time you put into this. The bearing idea and the flood idea are both worth thinking about. I am closing the pull request because the way they are built here does not match how Fieldwatch reads a radio.

Bearing. The great-circle bearing from the phone's GPS to an advertised latitude and longitude is a real number, and Remote ID is the radio that sends one. Classic radar still places every other dot by a hash of the MAC, so the dot stays put and is not a direction. Drawing a true bearing on the drones, with N, E, S, and W fixed to the top of the screen, would teach people that every dot is a direction. The radius is still loudness, so the picture also reads as distance. The screen labels are not the phone's heading, so a diamond at the top is north only when the phone is facing north.

The place that can carry a true bearing later is Reports → Path. That map is already north-up, and a Remote ID aircraft and its pilot are already drawn where they advertised. A bearing there would be the direction from you to that fix, and only for a radio that sent one.

Samsung and Meshtastic. Both are already stock signatures. Samsung SmartTags match company id 0x0075 and service FD5A. Meshtastic matches the name and UUID 6ba1b218. The new Samsung decoder treats a 6-byte 0x0075 payload as a SmartTag, SmartTag2, or offline beacon from the first byte. That company id is also phones and earbuds, so the detail line names a tag the packet did not identify. The Meshtastic decoder keys on 0xCBF0, which is not the UUID the catalog uses, and it prints the raw bytes as a node broadcast. A signature row can be muted. A hardcoded hint cannot.

Pairing flood. The thing you are describing is real. Flipper Zero, ESP32 Marauder, and Bruce broadcast a pairing popup and then change the address, over and over. The popup is a specific frame: Apple Continuity proximity pairing (0x004C, type 0x07) or Nearby Action (0x0F), a Fast Pair model id on service 0xFE2C, or Microsoft Swift Pair (0x0006). A real headphone keeps one address for minutes. The spam tools pick a new address about every packet.

The check in this pull request counts any new BLE radio with company id 0x004C or 0x0006, or any Fast Pair service data, if the hit count is low. A walk through a crowd produces that, because every iPhone uses 0x004C. The header then says BLE FLOOD, and the detail string names Flipper Zero or an ESP32 Marauder even though nothing in the advertisement identifies the tool.

A count that can work is a short window, about 10 seconds, of addresses that were heard once or twice and that carry one of those popup frames, sitting at about the same loudness. The honest line is "Many pairing advertisements from new addresses." The packet does not name the tool.

Thank you again for working through all three. I am going to leave the bearing on the path map, and a pairing-flood count, as things to consider later. This branch is not the way I want to land them.

@OffGridPete OffGridPete closed this Oct 2, 2026
@edwin042331-hue

Copy link
Copy Markdown
Author

Thanks for the thorough review and thoughtful feedback, @OffGridPete!

Your points on UX mental models make total sense—especially regarding the classic radar's loudness-vs-distance representation and keeping true bearing on the North-up Path map instead of creating false expectations on the polar plot. Also appreciate the insights on narrowing the BLE spam burst heuristics strictly to pairing frame types (0x07 / 0x0F / FastPair) to avoid crowd false positives.

Looking forward to seeing both concepts evolve in future Fieldwatch releases. Keep up the awesome work on this project!

Copy link
Copy Markdown
Owner

Hey — circling back on the flood-attack idea. I really liked it, and I went ahead and implemented a version. I'm testing it now, and as long as it passes field test it will be in the next release.

Fieldwatch watches the live list for a burst of brand-new Bluetooth addresses. It does not hide those radios, and it does not stop logging them. Wi-Fi is ignored. A repeat advertisement from an address already heard does not count again.

It keeps about the last ten seconds and runs two counts.

Pairing flood. Six or more new addresses in that window sent a pairing advertisement. The frames it counts are Apple proximity pairing, Apple Nearby Action, a three-byte Google Fast Pair model id, and Microsoft Swift Pair. Ordinary Apple Nearby Info and Find My are not in this count. The longer Fast Pair account-key advertisement is not either. That one is the already-paired chip, and Hide Fast Pair account-key is the switch for it.

Name flood. Fifteen or more new addresses in that window each advertised a Bluetooth name and were not one of those pairing frames.

Both counts also ask whether the addresses are about the same loudness. Fieldwatch takes the middle RSSI and keeps addresses within 12 dB of it. A room of phones at different distances does not trip the notice. When the burst is tight enough to count, the notice can say they are about one loudness, for example about −48 dBm, which reads as one nearby radio.

The first time a burst crosses the line, a dialog opens. The title is Pairing flood or Name flood. The text says how many new addresses, which pairing types were in the burst, and that a handheld such as a Flipper Zero, or an ESP32 running Marauder or Bruce, does this by advertising a pairing request or a new name and changing the address every packet. The advertisement itself does not name the tool. Continue closes the dialog. One line stays at the top of Live, such as “Pairing flood · 6 new addresses · about −48 dBm,” until those addresses age out of the ten-second window. The dialog does not open again for that same burst.

The same notice is written into reports, so the attack shows up there too, not only on Live.

Thanks again for the suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants