Received message path: Sender → hop1 (near sender) → ... → Us
Outbound path: Us → hop1 (near us) → ... → Contact
Was using outbound path which placed contacts near OUR repeater.
Now uses first hop of the received message path — the repeater
nearest to the sender. Only triggers on received messages, not sends.
When a contact has no GPS, estimate their position from RSSI
observations through multiple repeaters:
- 1 repeater: offset by RSSI distance in deterministic direction
- 2 repeaters: weighted midpoint between circle intersections
- 3+ repeaters: weighted centroid with inverse-square weighting
(closer repeater observations dominate)
Observations stored per-repeater (latest wins), max 8 per contact,
expire after 30 minutes. Each incoming message updates the estimate.
For contacts without GPS, estimate their position by:
1. Finding the first-hop repeater from their routing path
2. Estimating distance from RSSI using log-distance path loss model
3. Placing them at a deterministic offset from the repeater
Uses LoRa outdoor path loss exponent (n=3.0) with reference RSSI
-30dBm at 1m. Distance clamped to 10m-50km range. Bearing derived
from contact public key hash for stable positioning.
- maxRetryAttemptsFloodOnly: 1→0 (was causing an extra retry for
flood contacts that the official app doesn't do)
- isLastAttempt: fix off-by-one (retryAttempt already incremented
when called from _scheduleRetry)
- Reduce ACK timeout jitter from 1-8s to 0.5-2s to avoid long waits
that make the UI appear stuck
Replace hardcoded English strings in 32 Dart source files with l10n
references. Covers settings labels, UI actions, map/SAR features,
device config, notifications, profiles, spectrum scan, and status
messages for sl, de, hr, pl, es, fr, it, el, ru, tr, uk, zh, pt.
- Contact.isFavourite reads bit 0 of firmware flags field
- Toggle favourite via contact action sheet (writes to device)
- Favourites section shown first in contacts tab
- Path size options corrected to [1, 2, 3] bytes
- pushAdvert (0x80): import advert into firmware via cmdGetAdvertPath
before fetching contact, so name/type/location are available
- pushNewAdvert (0x8A): fire-and-forget importReceivedAdvert to store
contact in firmware (data already in push payload)
- controlData (0x8E) discovery: no firmware import (matches official app),
just lookup existing contacts and show generic labels for unknown nodes
- Update meshcore_client git ref to bcbeff0 (burst sync + advert import)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>