The N/E/S/W letters rode on the ring radius, so they overlapped the circle
as the card rotated. Place the letters on an outer radius and draw the ring
a glyph-height smaller, leaving a clear gap between them. The forward index
now sits in that gap, just outside the ring.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The no-heading hint was centred on the compass circle, overlapping the
ring. Draw it in the bottom readout band instead so the ring stays clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow the global Units preference in the two remaining Trail distance
spots that were still metric-only:
- Min dist gate: the index is now a unit-agnostic level (0=finest..3).
Metric stays 5/10/25/100 m; imperial uses round 15/30/75/300 ft (applied
as their metre equivalents in the sampling gate).
- Map scale grid: imperial mode uses round ft/mi steps (10 ft … 100 mi)
instead of metric steps with a converted label. Step storage moved to
float metres so sub-metre imperial steps keep their geometry; labels come
from a parallel table so the scale bar reads an exact round value.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the Settings › System "Units" (Metric/Imperial) row and update the
Trail action-menu entry from the old four-way km/h·mph·min/km·min/mi
"Units" cycle to a Speed/Pace "Readout" toggle within the global system.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a single "Units" choice (Settings › System: Metric / Imperial) that
governs every distance and speed shown in Tools — Nearby Nodes, the Trail
views, and the navigate-to-point screen.
- NodePrefs: units_imperial (global) + trail_show_pace (Trail readout mode),
schema bumped 0xC0DE0005 → 0006. The old combined trail_units_idx is kept
for file-layout stability but no longer drives anything; upgraders default
to metric + speed.
- geo::fmtDist gains an imperial mode (ft below ~1000 ft, then miles).
- NavView / Nearby / Trail (waypoint list, Summary distance, List deltas,
map scale-bar label) all format through the global preference.
- Trail's action-menu "Units" row becomes a Speed/Pace readout toggle; the
km-vs-mi unit now follows the global setting instead of being one of four
combined options.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The last stray getLocationProvider/isValid duplicate (with its own
ENV_INCLUDE_GPS guard). Fold it into ownPos so every GPS lookup in the
nav screens goes through UITask::currentLocation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The static haversineKm/bearingDeg/bearingCardinal/fmtDist members were
thin pass-throughs to GeoUtils. Remove them and qualify the call sites
with geo:: so there's no parallel local API to keep in sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
drawLine was duplicated verbatim in TrailScreen and CompassScreen, and
drawCircle lived only in CompassScreen. Lift both into a gfx:: namespace
header so the map and compass share one copy of each 1-px primitive.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
UITask::currentLocation() becomes the one place that reads the
LocationProvider fix. TrailScreen::ownPos, CompassScreen::gpsValid and
NearbyScreen::refresh now delegate to it instead of each duplicating the
getLocationProvider/isValid dance (and the ENV_INCLUDE_GPS guard).
pushCogFix now restarts the course-over-ground ring when the gap since the
previous fix exceeds 15 s: after losing GPS for a while, a window spanning
the stale old fixes would compute a bogus "teleport" heading. The
last-good heading is kept for display continuity.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clarify in both the Map view row and the Waypoints section that the
current GPS position and all waypoints are drawn continuously, so the Map
view is useful standalone without an active trail recording.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One character was too ambiguous to tell adjacent waypoints apart on the
map. Draw the first two characters of the label (falling back to one when
the label is a single char) in both the normal and degenerate map paths,
and widen the right-edge fit guard to the rendered text width.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the map collapses to one coordinate (e.g. a single waypoint with no
trail and no GPS fix yet), the degenerate branch drew the waypoint marker
but never its label — so it appeared as a bare diamond. Draw the first
character of the waypoint's label beside the centred marker, matching the
normal map path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Trail Map is now useful as a plain "you are here + waypoints" view even
when no trail is being recorded:
- The bounding box folds in the live GPS fix (via ownPos) alongside the
trail and waypoints, so the current position is always in frame.
- The current-position marker is drawn at the live fix when available
(falling back to the last recorded trail point otherwise), instead of
only at the trail's end — so it shows with an empty trail too.
- Empty-everything message is now "No GPS / no trail" and only appears when
there is genuinely nothing to show (no trail, no waypoints, no fix).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Trail action-menu table gains Mark here / Waypoints / Clear waypoints.
- New Waypoints section: dropping (Mark here + label), independence from
the trail (/waypoints, survives reboot/Reset), map marker, the always-
present Trail-start row, the two-bearing nav view, and rename/delete.
- New Compass section: heads-up course-over-ground (no magnetometer),
rotating card, movement-only heading, GPS-fix gating.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a bulk clear for saved waypoints (Trail → Hold Enter → Clear
waypoints), shown only when at least one waypoint exists. Independent of
the trail — Reset trail still leaves waypoints intact; this is the
explicit way to wipe them all instead of deleting one by one.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Compass: switch to a heads-up card. The direction of travel is now the
fixed "up" index at the top and the compass card rotates underneath it —
a North needle plus N/E/S/W letters placed at screen angle (bearing - cog),
so North spins as you turn rather than the arrow pointing at your course.
Trail waypoints: the nav list now always starts with a synthetic
"Trail start" row whenever a trail exists, so you can navigate back to
where you began without having marked it. The "Waypoints" action appears
when there are saved waypoints OR a trail to backtrack to. Rename/Delete
apply only to saved waypoints; the start row is navigate-only. List/nav
indexing goes through rowTarget()/wpIndex() helpers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Tools menu rendered all items from listStart() with no scroll, so the
6th entry (Compass) fell off the bottom on the OLED's short panel and was
unreachable. Add a scroll offset that keeps the selection in view, with
the usual ^/v indicators.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
startPingForKey() set the "RTT: ..." line but didn't rebuild the ping
menu, and updatePingMenuState only rebuilt on a string-count change it
computed after the mutation — so the menu stayed at a lone "Send" row and
nothing visibly happened after pressing Send, whether or not a reply came.
- Rebuild the menu right after starting the ping so "RTT: ..." shows at
once; on send failure show "RTT: send fail" instead of a blank.
- Sync the menu against its actual row count (_ping_menu._count) as result
lines populate, so RTT/SNR rows appear when the reply lands.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tools › Compass shows the device's course over ground (from the GPS COG
ring — no magnetometer) as a rotating arrow in a ring with a north tick,
plus a large degrees + cardinal readout. "No GPS fix" without a fix;
"move to set heading" when stationary (course undefined). Reuses
UITask::currentCourse, so it works without recording a trail.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two fixes from on-device testing:
- Node navigation was bound to a bare Enter in the detail view, which was
undiscoverable. Hold Enter now opens an Options popup (Navigate / Ping),
consistent with the rest of the UI. Navigate opens the shared NavView;
Ping runs the ping and shows its result.
- The ping popup always carried three result rows (RTT / SNR out / SNR
back) even before a ping, so the user could scroll onto blank rows. The
menu is now rebuilt to contain only "Send" plus the result lines that are
actually populated, growing in place as the reply arrives.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review fixes:
- NearbyScreen: when the periodic detail refresh can't find the selected
contact any more, clear _nav as well as _detail so the nav view and the
input handler don't disagree about state.
- TrailScreen: bound the label keyboard to WAYPOINT_LABEL_LEN-1 (11) chars
for both Mark-here and Rename, so the user can't type more than the stored
label holds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 6. In a node's detail view, Enter (when the ping popup is closed)
opens the shared NavView targeting that node's last-known advert position
— same To/Hdg/distance screen used by waypoints. Nodes with no position
report "No node GPS". This turns Nearby from a static snapshot into a
"walk toward this person" view and folds in the old compass-to-contact idea.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 5b. The Map view now overlays saved waypoints as a hollow-diamond
marker with the label's first character beside it. The bounding box folds
in every waypoint so off-track ones stay in frame, and the map renders
even when the trail is empty but waypoints exist. project() generalised to
projectLL(lat, lon) so the same projection draws track points and waypoints.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 5a. Trail → Hold Enter gains two rows:
- "Mark here": captures the current GPS fix and opens the keyboard for a
short label (empty → auto "WP<n>"); saved to the persistent table.
- "Waypoints" (shown when any exist): a list of saved waypoints with live
distance. Enter opens the shared NavView (To/Hdg/distance); Hold Enter
offers Rename / Delete.
All waypoint sub-screens layer over the trail views via _wp_mode and are
dismissed with Cancel. Map overlay comes next (5b).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 4. Header-only navview::draw() takes a target (lat, lon, label) plus
the caller's own fix and course-over-ground, and renders distance + two
absolute bearings (To / Hdg). Source-agnostic so Waypoints, Backtrack and
Nearby node nav all reuse it. Not wired in yet (phases 5-6).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 3. Fixed 16-entry table of (lat, lon, ts, label[12]) persisted to
/waypoints with a magic+version header (mirrors TrailStore's format).
Unlike the RAM-only trail, waypoints are loaded in UITask::begin() and
rewritten on change via UITask::saveWaypoints(), so they survive reboots.
add/remove/rename/clear operate on the in-RAM table; the screen layer
(phase 5) drives persistence after each edit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 2. A 5-fix GPS ring sampled every ~1 s in UITask::loop, filled
whenever there's a valid fix — independent of trail recording, so the
heading is available to navigation at all times, not only while logging
a trail.
- pushCogFix() rejects gross outliers (a fix implying > 50 m/s since the
previous one) so a single bad GPS reading can't swing the heading.
- currentCourse(deg) returns the bearing across the window (oldest→newest)
once the window spans ≥ 6 m of real movement; below that it holds the
last good heading, and returns false only until the first valid heading
exists. This is the "two absolute bearings (To / Hdg)" heading source
for the upcoming nav view.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 of the navigation feature. The geographic helpers were private
statics in NearbyScreen; move them to a header-inline geo:: namespace so
the upcoming Waypoints / course-over-ground code can reuse them. NearbyScreen
keeps thin forwarders so its call sites are unchanged. No behaviour change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On a tall portrait e-ink panel the clock and lock screens wasted most of
the vertical space on a small inline "HH:MM". Render HH and MM on two
lines in a new size-4 font (built-in GFX scaled 7×, ~42×56 px) so the
digits roughly double in height and fill the narrow width. Wide panels
(OLED, landscape e-ink) keep the classic single-line size-2 layout.
- New shared drawClockTime() helper used by both the Clock home page and
the lock screen; returns the y below the time block so the date and
dashboard rows flow beneath it.
- GxEPDDisplay: size 4 = built-in font × BIG_TEXT_SCALE (7); getCharWidth /
getLineHeight / setTextSize handle it; fontAscender stays 0 (built-in is
top-left origin).
- Centre the big digits on their visible glyph width — the built-in font
advances 6 px per char but the glyph is 5 px wide, so getTextWidth over-
reports by one trailing column and the digits would sit ~half a column
left of centre. AM/PM rendered one size larger (size 2).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>