Files
MeshCore-Solo/examples/companion_radio/Waypoint.h
MarekZegare4 b8a7b5dcc0 refactor: dedup persistence header, harden action menu, tidy small items
- Persist.h: shared writeHeader()/readHeader() for the magic+version+count
  on-disk header; Waypoint and Trail snapshots now share one implementation
  (byte layout unchanged; each keeps its own count clamp/reject + extra fields).
- TrailScreen: pushAction() guards _act_map / PopupMenu capacity, so adding a
  new action can't silently overrun the fixed array.
- PopupMenu: add count()/setSelected() accessors; replace the remaining direct
  _sel/_count field pokes in NearbyScreen, TrailScreen and QuickMsgScreen.
- Trail summary: unit-suffixed time ("1h 05m" / "5m 03s") so hours can't be
  misread as minutes.
- Trail::haversineMeters delegates to geo::haversineKm — single Haversine
  implementation (also trims a little flash).

Verified: GAT562 (SSD1306) and WioTrackerL1Eink (GxEPD) solo builds compile clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 13:37:03 +02:00

93 lines
2.9 KiB
C++

#pragma once
// Saved GPS waypoints — small fixed table persisted to /waypoints. Unlike the
// trail (a RAM ring), waypoints are long-lived: loaded at boot, rewritten on
// every add / delete / rename. Used by the navigation feature to mark a spot
// (car, camp, water…) and later get bearing + distance back to it.
#include <Arduino.h>
#include <string.h>
#include "Persist.h"
static const uint8_t WAYPOINT_LABEL_LEN = 12; // incl. NUL → 11 visible chars
struct Waypoint {
int32_t lat_1e6, lon_1e6;
uint32_t ts; // RTC time when marked
char label[WAYPOINT_LABEL_LEN];
};
class WaypointStore {
public:
static const int CAPACITY = 16;
int count() const { return _count; }
bool full() const { return _count >= CAPACITY; }
const Waypoint& at(int i) const { return _wp[i]; }
// Add a waypoint; label may be empty (caller can auto-name). Returns false
// if the table is full. Trims/copies the label safely.
bool add(int32_t lat, int32_t lon, uint32_t ts, const char* label) {
if (_count >= CAPACITY) return false;
Waypoint& w = _wp[_count++];
w.lat_1e6 = lat; w.lon_1e6 = lon; w.ts = ts;
setLabel(w, label);
return true;
}
void remove(int i) {
if (i < 0 || i >= _count) return;
for (int j = i; j < _count - 1; j++) _wp[j] = _wp[j + 1];
_count--;
}
void rename(int i, const char* label) {
if (i < 0 || i >= _count) return;
setLabel(_wp[i], label);
}
void clear() { _count = 0; }
// ── persistence ──────────────────────────────────────────────────────────
// Layout: 4-byte magic "WYPT", uint8 version, uint8 reserved, uint16 count,
// then `count` raw Waypoint records.
static const uint32_t SAVE_MAGIC = 0x54505957; // "WYPT" little-endian
static const uint8_t SAVE_VERSION = 1;
template <typename F>
bool writeTo(F& file) {
if (!persist::writeHeader(file, SAVE_MAGIC, SAVE_VERSION, (uint16_t)_count)) return false;
for (int i = 0; i < _count; i++) {
if (file.write((uint8_t*)&_wp[i], sizeof(Waypoint)) != sizeof(Waypoint)) return false;
}
return true;
}
template <typename F>
bool readFrom(F& file) {
uint16_t cnt = 0;
if (!persist::readHeader(file, SAVE_MAGIC, SAVE_VERSION, cnt)) return false;
if (cnt > CAPACITY) cnt = CAPACITY;
_count = 0;
for (uint16_t i = 0; i < cnt; i++) {
Waypoint w;
if (file.read((uint8_t*)&w, sizeof(Waypoint)) != (int)sizeof(Waypoint)) break;
w.label[WAYPOINT_LABEL_LEN - 1] = '\0'; // defensive
_wp[_count++] = w;
}
return true;
}
private:
Waypoint _wp[CAPACITY];
int _count = 0;
static void setLabel(Waypoint& w, const char* label) {
if (label && label[0]) {
strncpy(w.label, label, WAYPOINT_LABEL_LEN - 1);
w.label[WAYPOINT_LABEL_LEN - 1] = '\0';
} else {
w.label[0] = '\0';
}
}
};