Files
planeMapper/_bmad-output/planning-artifacts/prd.md
T
2026-04-22 21:18:42 -04:00

15 KiB
Raw Blame History

stepsCompleted, inputDocuments, workflowType, classification
stepsCompleted inputDocuments workflowType classification
step-01-init
step-02-discovery
step-02b-vision
step-02c-executive-summary
step-03-success
step-04-journeys
step-05-domain
step-06-innovation
step-07-project-type
step-08-scoping
step-09-functional
step-10-nonfunctional
step-11-polish
step-12-complete
README.md
CLAUDE.md
prd
projectType domain complexity projectContext
iot_embedded aerospace medium greenfield

Product Requirements Document - planeMapper

Author: Matt Edholm Date: 2026-04-22

Executive Summary

planeMapper is a permanently offline, purpose-built ADS-B radar display in a picture-frame form factor. It receives live aircraft positions via an RTL-SDR dongle, renders them on an OpenStreetMap base map centred on the user's home location, and pushes the result to a Waveshare 7.3" 6-colour e-ink display every 60 seconds. Designed to be wall-mounted or desk-standing — always on, always current, zero interaction required after initial setup.

Target user: Aviation enthusiasts, pilots, and spotters who want persistent ambient awareness of local air traffic without a phone, tablet, or cloud service.

What Makes This Special

planeMapper is an instrument, not an app. Every competing solution requires a screen to be unlocked and an app opened. planeMapper is simply there — glanceable, ambient, contextual. The e-ink display draws no power between refreshes and reads like a framed instrument, not a consumer screen.

The defining product principle is permanent offline operation. WiFi is used only during initial provisioning to download map tiles, then killed at the radio level. No cloud dependency, no subscription, no data egress — ever. This is an architectural decision elevated to a product identity.

Setup is via a captive portal hotspot broadcast by the device on first boot. The user enters their home address or ICAO code, home WiFi credentials, and coverage radius. The device provisions itself, downloads and validates tiles, kills the WiFi radio, and enters operational mode. A recessed GPIO button on the rear housing triggers reset; an LED provides immediate confirmation.

Success Criteria

User Success

  • Aircraft overhead or nearby are identifiable at a glance
  • Callsign, altitude, and aircraft type are readable at normal desk/wall viewing distance
  • Position trail conveys direction of travel without requiring interpretation
  • Colour coding and type icons are immediately understood without reference

Technical Success

  • Display refreshes every 60 seconds without failure or intervention
  • Device returns to operational state after power cycling without reconfiguration
  • Stale data displayed gracefully on dump1090 decode failure — no crashes, no blank display
  • Setup completes successfully from a fresh SD card flash
  • WiFi radio confirmed off after provisioning

Measurable Outcomes

  • 60s refresh cycle sustained without manual intervention
  • Aircraft visible on display within 2 minutes of first boot post-setup
  • 72-hour continuous operation without restart

Product Scope

MVP (Phase 1)

  • Captive portal setup: ICAO code or address/postcode input, WiFi credentials, coverage radius
  • ICAO lookup via bundled OurAirports database; address geocoding via Nominatim
  • OSM tile download, cache size validation, cache completeness validation, WiFi radio kill via rfkill
  • Reset button (3s hold) + LED immediate feedback
  • 60s refresh loop, resuming automatically after power cycle
  • Startup screen displayed during boot before first radar render
  • Live aircraft: heading arrow, callsign, altitude label
  • 5-dot position trail (oldest dot smallest)
  • Altitude colour bands (6 colours across altitude ranges)
  • Aircraft type icons: GA/light, commercial/large, helicopter, private jet via ADS-B category + callsign pattern; altitude-based fallback when type unknown (GA <10,000ft, private jet 10,00030,000ft, airliner >30,000ft)
  • MLAT positions visually distinguished from directly received positions
  • Stale data detection, retention, and visual indicator on decode failure
  • Airspace circular boundaries, outline only (OpenAIP data)
  • Home location marked on map

Growth (Phase 2)

  • Own squawk code highlighting
  • Aircraft size coding (heavy/medium/light)
  • Airspace colour fills

Vision (Phase 3)

  • Packaged enclosure/frame design for gifting or small-batch sale

User Journeys

Journey 1 — First Setup (Success Path)

Matt flashes the SD card and powers on the device. His phone picks up planeMapper-setup. He connects; the captive portal opens automatically. He types his postcode, hits search — the device resolves it to coordinates and confirms the location. He sets radius to 100nm, enters his home WiFi credentials, hits Confirm. The browser shows "Downloading map data — this takes a few minutes. Do not power off." The LED pulses. Minutes later the e-ink flickers and settles on a live radar view. Aircraft are visible. Done.

Capabilities: captive portal, ICAO/address resolution, WiFi credential handling, tile provisioning, cache validation, LED feedback, first render.

Journey 2 — Daily Use (Happy Path)

It's a Tuesday afternoon. Matt hears a low rumble and glances at the display. A helicopter is northeast of home at 1,500ft, tracking southwest — the dot trail confirms it's been moving for a few minutes. Below it, a commercial flight at FL280 crossing his coverage area with a BA callsign. He goes back to work. The display asked nothing of him.

Capabilities: 60s refresh, heading arrows, dot trail, altitude colour bands, callsign labels, aircraft type icons.

Journey 3 — Decode Gap / Stale Data (Edge Case)

dump1090 has a bad few minutes — the dongle got bumped. The display refreshes but positions haven't updated. Aircraft from the last good decode remain visible but visually dimmed. Matt notices, reseats the dongle. Next refresh cycle, positions update normally. No crash, no blank screen, no intervention required.

Capabilities: stale data detection, visual stale indicator, graceful degradation, recovery on next good decode.

Journey 4 — Reconfiguration / Reset

Matt moves house. He holds the reset button for three seconds. The LED lights immediately. The e-ink refreshes to the setup screen. He connects his phone and goes through setup with the new address. Same flow as Journey 1.

Capabilities: GPIO button hold detection, LED immediate feedback, config wipe, return to captive portal state.

Journey Requirements Summary

Capability Journeys
Captive portal + location resolution 1, 4
Tile provisioning + cache validation + WiFi kill 1
LED feedback 1, 4
Live aircraft rendering (arrow, label, trail, colour, type) 2
60s refresh loop 2
Stale data handling 3
Reset button + config wipe 4

Domain-Specific Requirements

ADS-B Data

  • dump1090 data is best-effort — callsign, category, and altitude may be absent. The renderer handles missing fields gracefully without crashing
  • ADS-B positions are self-reported — no ground-truth validation. Phantom or misplaced positions are possible; the display makes no attempt to filter or correct them
  • MLAT positions are estimated, not directly received, and may be less accurate. Visually distinguished when the MLAT flag is present in the dump1090 feed

Airspace Data

  • OpenAIP provides published airspace boundaries (GeoJSON/OpenAir) representing published structures, not real-time restrictions. Purely informational.

Compliance

None. planeMapper is a passive receiver display. It receives and renders publicly broadcast radio signals. No licensing, certification, or data handling obligations apply.

Innovation & Novel Patterns

Permanently Offline IoT Device

planeMapper inverts the dominant IoT trend. Consumer IoT devices move toward cloud dependency; planeMapper eliminates WiFi at the radio level post-provisioning. No cloud, no subscription, no data egress — ever. This is an architectural decision that defines the product identity.

E-Ink as Ambient Instrument

E-ink is chosen for aesthetics, not power savings. It looks like a framed instrument, not a consumer screen. The display constraint becomes the product's most distinctive quality.

Market Position

No direct competitor exists in this form. ADS-B displays require either a connected device (apps) or expensive dedicated avionics. The permanently offline picture-frame niche is unoccupied.

Primary Risk

The tile cache is the single dependency for the offline principle. Mitigation: validate cache completeness and size at end of provisioning before killing the WiFi radio. On failure, remain in provisioning state and prompt retry.

IoT/Embedded Specific Requirements

Hardware

Component Spec
SBC Raspberry Pi Zero 2W
SDR Nooelec NESDR Smart v5 (SMA, 0.5PPM TCXO)
Antenna FlightAware 1090MHz, 5.5dBi
Display Waveshare 7.3" 6-colour e-ink HAT (Spectra 6, 800×480, SPI)
Reset Momentary push button, GPIO input, recessed on rear housing
Feedback Single LED, GPIO output, rear housing adjacent to button
USB Micro-USB OTG adapter required — RTL-SDR occupies the Zero 2W's single USB port

Device State Machine

State WiFi Description
Provisioning AP mode (planeMapper-setup) First boot or post-reset; captive portal active
Provisioning (downloading) Client mode Joined home WiFi; downloading and validating tiles
Operational Off (rfkill) Normal radar display; no network access
Reset triggered Re-enabled Returns to Provisioning state

Power Profile

  • Mains powered — no battery
  • Pi Zero 2W: ~500mA at 5V idle
  • E-ink: power draw only during ~30s refresh cycle, zero between refreshes

Security Model

  • WiFi off in operational state — network attack surface is zero
  • Captive portal during provisioning is open, local-only, and short-lived
  • Config file (home coordinates, WiFi credentials) stored plaintext on SD card — acceptable for personal device

Update Mechanism

None. SD card reflash is the update path if needed.

Functional Requirements

Device Setup & Provisioning

  • FR1: The device broadcasts a WiFi hotspot on first boot and after reset
  • FR2: The user can connect to the device hotspot and be served a setup interface automatically (captive portal)
  • FR3: The user can enter a location as an ICAO code or address/postcode
  • FR4: The device resolves an ICAO code to coordinates using a bundled airport database
  • FR5: The device resolves an address or postcode to coordinates using a geocoding service
  • FR6: The device displays the resolved location for user confirmation before proceeding
  • FR7: The user can set a coverage radius
  • FR8: The user can enter home WiFi credentials during setup
  • FR9: The device connects to the user's home WiFi and downloads and caches map tiles for the configured area
  • FR9a: After tile download, the device validates cache completeness and size before killing the WiFi radio; on failure, the device remains in provisioning state and prompts retry
  • FR10: The device kills the WiFi radio after successful provisioning
  • FR11: The setup interface confirms provisioning status to the user before the WiFi hotspot is dropped

Reset & Recovery

  • FR12: The user can trigger a device reset by holding the reset button for 3 seconds
  • FR13: The device provides immediate visual feedback via LED when a reset hold is detected
  • FR14: A confirmed reset wipes device configuration and returns to provisioning state
  • FR15: The device displays a setup screen on the e-ink display after reset

Map Display

  • FR16: The device renders an OpenStreetMap base map centred on the configured home location
  • FR17: The map covers the configured coverage radius
  • FR18: The home location is marked as a distinct point on the map
  • FR19: Airspace circular boundaries are rendered as outlines on the map (OpenAIP data)

Aircraft Display

  • FR20: The device fetches live aircraft data from the dump1090 JSON feed
  • FR21: Each aircraft is rendered at its current position with a heading arrow aligned to direction of travel
  • FR22: Each aircraft displays its callsign and altitude as a label
  • FR23: Each aircraft is colour-coded by altitude band
  • FR24: Each aircraft is rendered with a type-specific icon determined from ADS-B category data or callsign pattern matching (GA/light, commercial/large, helicopter, private jet)
  • FR24a: When aircraft type cannot be determined, icon is assigned by altitude — GA below 10,000ft, private jet 10,00030,000ft, airliner above 30,000ft
  • FR25: Each aircraft displays a trail of up to 5 previous positions as dots, oldest dot smallest
  • FR26: Aircraft transmitted via MLAT are visually distinguished from directly received aircraft

Stale Data Handling

  • FR27: The device detects when the dump1090 feed has not produced a fresh decode
  • FR28: Aircraft from the last successful decode are retained on display and visually marked as stale
  • FR29: Aircraft positions are restored to normal display state when fresh decode data is received

Refresh Loop & Boot

  • FR30: The display refreshes on a 60-second cycle
  • FR31: The device continues the refresh loop indefinitely without manual intervention
  • FR32: The device resumes the refresh loop automatically after power cycling
  • FR33: The device displays a defined startup screen during boot, before the first radar render is complete

Non-Functional Requirements

Performance

  • Full radar render must complete within 45 seconds on Pi Zero 2W hardware
  • Base map tile layer is pre-composited and cached in memory between refresh cycles — only the aircraft overlay is re-rendered each cycle
  • dump1090 JSON fetch must complete within 5 seconds; timeout triggers stale data path
  • E-ink SPI transfer initiates only after render pipeline is complete

Reliability

  • Refresh loop must sustain 72+ hours of continuous operation without restart or intervention
  • Device must recover to operational state within 5 minutes of unclean power loss, without manual intervention
  • dump1090 decode failure must not crash the refresh loop

Storage

  • OSM tile cache must not exceed 2GB for any supported coverage radius, ensuring viability on a 16GB SD card alongside OS and software
  • Cache size validated during provisioning before WiFi radio is killed

Integration

  • dump1090 JSON feed: http://localhost:8080/data/aircraft.json — local, no authentication
  • Nominatim geocoding API: single call during provisioning; internet required at that point only
  • OurAirports database: bundled with software, no runtime dependency
  • OpenAIP airspace data: fetched and cached during provisioning alongside OSM tiles

Security

  • WiFi radio off in operational state — network attack surface is zero
  • No external network calls in operational state
  • Config stored plaintext on SD card — acceptable for personal single-user device