Skip to content

[ON HOLD] FEATURE: GPS proximity circuit auto-detection (instant, no lap required) #35

Description

@FabioRoss

What problem are you trying to solve?

Setting up sector lines before every session is error-prone and inconvenient.
Users shouldn't have to hardcode GPS coordinates in their sketch if they race
at known circuits. And on competition day, the timer needs to be ready
immediately — not after driving a detection lap.

The upstream CourseDetector already solves multi-layout detection via driven
distance (drive ~1 lap, compare odometer to each course's expected length).
This is excellent for venues with multiple layouts where distance is the
discriminator.

But there's a simpler, faster alternative for single-layout venues or any
situation where GPS coordinate proximity is sufficient:
"I'm within X meters of circuit center point A → load circuit A's sector lines."

This works on the FIRST GPS fix after power-on — no driving required.

Proposed solution

Add a GPS proximity scan that finds the nearest pre-configured circuit and
auto-loads its sector definitions.

KEY METHOD:

const CircuitStruct* findClosestCircuit(double currentLat, double currentLon);
  • Scans a PROGMEM database of CircuitStruct entries
  • For each: haversine(currentLat, currentLon, circuit.lat, circuit.lon)
  • Returns pointer to nearest circuit entry (always returns SOMETHING)
  • Caller decides whether the returned circuit is "close enough" via their
    own distance threshold (or just trust it if only one circuit is in the DB)

CIRCUIT LOADING:

void loadCircuit(const CircuitStruct* ptr);
  • deep-copies circuit data from PROGMEM into RAM
  • dynamically allocates sector and service sector arrays (malloc)
  • sets circuitLoaded = true
  • gates loop() processing until circuitLoaded

CIRCUIT DATA STRUCTURE (PROGMEM-stored):

struct CircuitStruct {
  const char* name;
  double lat, lon;           // circuit center point for proximity detection
  uint8_t numSectors;
  const SectorStruct* sectors;          // sector line GPS coordinates
  uint8_t numServiceSectors;
  const serviceSectorStruct* serviceSectors;
};

ADDITIONAL METHODS:

void reloadCircuit();     // clears circuitLoaded, triggers re-detection
bool circuitLoaded;       // public readable — safe to check before loop()

COMPARED TO CourseDetector:

CourseDetector (upstream):
- Detects via DRIVEN DISTANCE (compare odometer to course.lengthFt ±25%)
- Requires completing ~1 lap before detection resolves
- Better for multi-layout tracks at same venue (same GPS location, different distance)
- Handles up to MAX_COURSES simultaneously

GPS Proximity (this proposal):
- Detects via GPS COORDINATE DISTANCE (haversine to circuit center)
- Works on FIRST GPS FIX — instant
- Better for known single-layout circuits where GPS proximity is unambiguous
- Simpler implementation, less RAM overhead

These are COMPLEMENTARY approaches. A possible architecture: try GPS proximity
first (instant, good enough for 90% of cases); fall back to CourseDetector if
multiple circuits are within the proximity threshold.

CIRCUIT DATABASE DESIGN:

The database should live in user-provided PROGMEM in the sketch or a separate
.h file (not hardcoded inside the library itself). The library provides:
- The CircuitStruct/SectorStruct data types
- findClosestCircuit() and loadCircuit() methods
- The user provides their own array of CircuitStructs

This keeps the library hardware-agnostic and geography-agnostic — the library
doesn't need to know about any specific track.

Alternatives considered

  1. CourseDetector (upstream's current approach) — driven-distance based.
    Works great for multi-layout tracks but requires completing a detection lap.
    Complement, not replacement.

  2. User calls setStartFinishLine() / setSector2Line() / setSector3Line() manually —
    current baseline. Works but requires hardcoding coordinates in the sketch.
    Error-prone if the user races at multiple circuits.

  3. Automatic on every GPS fix inside loop() — what RaceBoard does. Less suitable
    for the library itself; better as a user-implemented pattern using
    findClosestCircuit() as the building block.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions