|
| 1 | +# V1.4 Design — Extended Range + Android Production |
| 2 | + |
| 3 | +**Date:** 2026-03-28 |
| 4 | +**Status:** Approved |
| 5 | +**Milestone:** V1.4 — Android Launch + Extended Range (Q3 2026) |
| 6 | + |
| 7 | +## Summary |
| 8 | + |
| 9 | +V1.4 adds 5 features: BLE Long Range (Coded PHY) for 400m-1km range, connection quality indicators, FixPhrase 4-word location encoding, OpenStreetMap offline tile downloads, and Android production release. F-Droid deferred to v1.5. |
| 10 | + |
| 11 | +## Features |
| 12 | + |
| 13 | +### 1. BLE Long Range (Coded PHY) |
| 14 | + |
| 15 | +**Goal:** Extend Field Link range from ~30-50m to 400m-1km using BLE 5.0 Coded PHY. |
| 16 | + |
| 17 | +**Approach:** Native platform channels (no flutter_blue_plus fork). |
| 18 | + |
| 19 | +**Android (Kotlin):** |
| 20 | +- New method channel: `com.redgrid.link/ble_phy` |
| 21 | +- Methods: `requestCodedPhy(deviceAddress)`, `isCodedPhySupported()`, `getCurrentPhy(deviceAddress)` |
| 22 | +- Implementation: `BluetoothGatt.setPreferredPhy(PHY_LE_CODED, PHY_LE_CODED, PHY_OPTION_S8)` |
| 23 | +- S8 coding = maximum range (~4x standard BLE), 125 kbps throughput |
| 24 | +- Requires Android 8.0+ (API 26) and hardware support |
| 25 | +- Runtime check: `BluetoothAdapter.isLeCodedPhySupported()` |
| 26 | + |
| 27 | +**iOS (Swift):** |
| 28 | +- CoreBluetooth auto-negotiates Coded PHY when both devices support it (iPhone 8+, iOS 14+) |
| 29 | +- No explicit PHY selection API — detect active PHY from connection events |
| 30 | +- Lighter implementation than Android |
| 31 | + |
| 32 | +**Dart layer:** |
| 33 | +- New `BlePhyService` wrapping platform channels |
| 34 | +- `BleTransport` calls `requestCodedPhy()` after successful connection |
| 35 | +- Silent fallback if device doesn't support Coded PHY |
| 36 | +- Expose `isLongRange` boolean per peer connection |
| 37 | + |
| 38 | +**UI:** |
| 39 | +- "LR" badge on peer cards when Coded PHY is active |
| 40 | +- Info tooltip explaining range improvement |
| 41 | + |
| 42 | +**Trade-off:** Coded PHY S8 halves throughput (125 kbps vs 1 Mbps). Acceptable for position updates (<200 bytes). Large payloads use P2P transport which is unaffected. |
| 43 | + |
| 44 | +### 2. Connection Quality Indicator (RSSI) |
| 45 | + |
| 46 | +**Goal:** Live signal strength display for each connected peer. |
| 47 | + |
| 48 | +**Approach:** Extend existing RSSI infrastructure (currently one-shot during discovery). |
| 49 | + |
| 50 | +- Add periodic RSSI polling timer: every 5 seconds per connected peer |
| 51 | +- Rolling average of last 5 readings to smooth fluctuations |
| 52 | +- New `connectionQualityProvider` (Riverpod StateNotifierProvider) keyed by peer ID |
| 53 | +- 4-tier signal display (reuse existing color bar widget from `SessionJoinCard`) |
| 54 | +- Range warning: notification when any peer drops below -85 dBm for 3+ consecutive readings |
| 55 | + |
| 56 | +**Display locations:** |
| 57 | +- Peer HUD overlay on map |
| 58 | +- Team roster bottom sheet |
| 59 | +- Session status bar |
| 60 | + |
| 61 | +### 3. FixPhrase (4-Word Location Encoding) |
| 62 | + |
| 63 | +**Goal:** Encode any lat/lon as 4 hyphenated English words (~11m accuracy). Order-independent. |
| 64 | + |
| 65 | +**Approach:** Port from RedGridMGRS JavaScript implementation to Dart. |
| 66 | + |
| 67 | +**Algorithm:** |
| 68 | +1. Shift coordinates to positive integers: `latInt = (lat * 10000 + 900000).round()`, `lonInt = (lon * 10000 + 1800000).round()` |
| 69 | +2. Convert to 7-digit zero-padded strings |
| 70 | +3. Extract 4 groups with non-overlapping index ranges: |
| 71 | + - g0: 0-1999 (latitude high bits) |
| 72 | + - g1: 2000-5609 (longitude high bits) |
| 73 | + - g2: 5610-6609 (mid-precision) |
| 74 | + - g3: 6610-7609 (full precision) |
| 75 | +4. Map each group index to wordlist |
| 76 | + |
| 77 | +**Precision modes:** |
| 78 | +- 2 words: ~1.1km accuracy |
| 79 | +- 3 words: ~110m accuracy |
| 80 | +- 4 words: ~11m accuracy |
| 81 | + |
| 82 | +**Key property:** Words can be provided in ANY order due to non-overlapping index ranges. |
| 83 | + |
| 84 | +**Assets:** |
| 85 | +- `assets/fixphrase_wordlist.json` — 7,611 English words (BSD-3-Clause, Netsyms Technologies) |
| 86 | + |
| 87 | +**Files:** |
| 88 | +- `lib/utils/fixphrase.dart` — encode/decode functions |
| 89 | +- Integration into Coordinate Converter tool |
| 90 | +- Integration into coordinate display format selector |
| 91 | + |
| 92 | +### 4. OpenStreetMap Offline Tile Source |
| 93 | + |
| 94 | +**Goal:** Allow downloading OSM tiles for offline use (currently only OpenTopoMap downloadable). |
| 95 | + |
| 96 | +**Approach:** Minimal change to existing tile infrastructure. |
| 97 | + |
| 98 | +- `TileSources.osm` already works for online viewing |
| 99 | +- Add `tileSource` parameter to `TileManager.downloadRegion()` |
| 100 | +- Update `MapDownloadSheet` with source selector (OSM / OpenTopoMap / USGS Topo) |
| 101 | +- Store source type in `MapRegion` database row for correct attribution when viewing offline |
| 102 | +- ~30 lines of logic change, mostly UI |
| 103 | + |
| 104 | +### 5. Android Production Release (DEFERRED) |
| 105 | + |
| 106 | +**Deferred until LLC/business account conversion.** Once Estus Holdings LLC is approved and both Apple (ASC) and Google (Play Console) accounts are converted to business/organization accounts, promote to Production track. This bypasses TestFlight external tester review on iOS and provides proper business identity on both stores. |
| 107 | + |
| 108 | +### 6. F-Droid (DEFERRED to v1.5) |
| 109 | + |
| 110 | +**Rationale:** F-Droid requires removing Google Play Services (Nearby Connections API) and Play Billing. This is a significant architecture change that would block the v1.4 production launch. Better as a dedicated effort. |
| 111 | + |
| 112 | +## Implementation Order |
| 113 | + |
| 114 | +1. **FixPhrase** — Pure Dart, no platform risk, immediately testable |
| 115 | +2. **OSM tile source** — Small change, high user value |
| 116 | +3. **Connection quality indicator** — Wiring existing pieces |
| 117 | +4. **BLE Long Range (Coded PHY)** — Native platform work, highest complexity |
| 118 | + |
| 119 | +Android production release deferred until business account conversion. |
| 120 | + |
| 121 | +## Non-Goals |
| 122 | + |
| 123 | +- F-Droid submission (v1.5) |
| 124 | +- Meshtastic integration (v2.0) |
| 125 | +- ATAK interoperability (v2.0) |
| 126 | +- Route planning (v2.1) |
| 127 | + |
| 128 | +## Testing Strategy |
| 129 | + |
| 130 | +- Unit tests for FixPhrase encode/decode (round-trip, edge cases, word order independence) |
| 131 | +- Unit tests for RSSI rolling average and threshold logic |
| 132 | +- Widget tests for connection quality display |
| 133 | +- Integration tests for Coded PHY platform channel (mock channel responses) |
| 134 | +- Manual testing: two physical BLE 5.0 devices for Coded PHY range verification |
0 commit comments