DovesLapTimer 4.3.0
GPS-based lap timing Arduino library — go-karts to race cars
Loading...
Searching...
No Matches
CrossingEngine.h
Go to the documentation of this file.
1
21#ifndef _DOVES_CROSSING_ENGINE_H
22#define _DOVES_CROSSING_ENGINE_H
23
24#include <Arduino.h>
25// Polyfills std::forward (used by the debug templates below) on cores with
26// no C++ standard library — classic AVR. Must come before any std:: use so
27// this header is safe to include in any order relative to DovesLapTimer.h.
28#include "ArxTypeTraits.h"
29#include "GeoMath.h"
30
31#define CROSSING_LINE_SIDE_A -1
32#define CROSSING_LINE_SIDE_EXACT 0
33#define CROSSING_LINE_SIDE_B 1
34
35// GPS time base: milliseconds since UTC midnight, wraps 86,399,999 -> 0
36#define DOVES_MILLIS_PER_DAY 86400000UL
37
38// Max believable gap between the two consecutive buffered fixes that straddle
39// a crossing line. A larger gap means the GPS time base stepped (e.g. u-blox
40// re-acquisition) and the pair cannot be interpolated coherently.
41#define CROSSING_MAX_FIX_GAP_MS 10000UL
42
43// The straddling pair's summed distance to the line is validated against
44// max(crossingThresholdMeters, factor * pair spacing) so that low-rate GPS
45// (widely spaced fixes) doesn't fail validation purely on sample density.
46#define CROSSING_PAIR_SPACING_FACTOR 1.25
47
59static inline unsigned long timeSinceMidnightDelta(unsigned long startMs, unsigned long endMs) {
60 if (endMs < startMs) {
61 endMs += DOVES_MILLIS_PER_DAY;
62 }
63 return endMs - startMs;
64}
65
66// Outcome of a single CrossingEngine::detect pass.
68 LINE_DETECT_NONE, // not in / near the zone, or exited with an invalid interpolation
69 LINE_DETECT_IN_ZONE, // inside the zone (entered this fix or continuing)
70 LINE_DETECT_COMPLETED, // exited the zone this fix — interpolated outputs valid
71};
72
74 double lat; // latitude
75 double lng; // longitude
76 unsigned long time; // current time in milliseconds
77 float odometer; // time traveled since device start and this entry
78 float speedKmh; // speed in kmph
79};
80
82public:
83 CrossingEngine(double crossingThresholdMeters = 7, Stream *debugSerial = NULL);
84
88 void setThreshold(double meters);
92 void setDebugSerial(Stream *debugSerial);
97 void setForceLinear(bool linear);
98 bool getForceLinear() const;
106 unsigned int getRejectedCrossingCount() const;
111 void reset();
112
138 double currentLat, double currentLng,
139 unsigned long currentTimeMs,
140 float currentOdometer,
141 float currentSpeedKmh,
142 const crossingPointBufferEntry *prevFix,
143 double pointALat, double pointALng,
144 double pointBLat, double pointBLng,
145 bool& crossingFlag,
146 int lineLabel,
147 double& outLat, double& outLng,
148 unsigned long& outTime,
149 double& outOdometer);
150
151private:
152#ifdef DOVES_DISABLE_DEBUG
153 // Compile-time debug kill switch: the runtime _serial null-check still
154 // keeps every debug string and print call-site in flash on production
155 // builds that never attach a debug Stream. Defining DOVES_DISABLE_DEBUG
156 // (e.g. -DDOVES_DISABLE_DEBUG in build flags) replaces the debug
157 // templates with empty inlines so the compiler drops it all — several
158 // KB on a fully-featured firmware. Debug behavior is unchanged when
159 // the macro is not defined.
160 template<typename... Args> void debug_print(Args&&...) {}
161 template<typename... Args> void debug_println(Args&&...) {}
162#else
163 template<typename... Args>
164 void debug_print(Args&&... args) {
165 if(_serial) { _serial->print(std::forward<Args>(args)...); }
166 }
167 template<typename... Args>
168 void debug_println(Args&&... args) {
169 if(_serial) { _serial->println(std::forward<Args>(args)...); }
170 }
171#endif
172
178 bool interpolateCrossingPoint(double& crossingLat, double& crossingLng, unsigned long& crossingTime, double& crossingOdometer, double pointALat, double pointALng, double pointBLat, double pointBLng);
182 double interpolateWeight(double distA, double distB, float speedA, float speedB);
186 double catmullRom(double p0, double p1, double p2, double p3, double t);
187
188 Stream *_serial;
189 double crossingThresholdMeters;
190 bool forceLinear = true;
191 unsigned int rejectedCrossingCount = 0;
192
193 // Buffer sizing: AVR exposes RAMEND/RAMSTART so we can tell a Mega (8KB) from a Uno (2KB).
194 // On modern 32-bit cores (nRF52, ESP32, SAMD, RP2040, etc.) those macros are undefined
195 // and would silently evaluate to 0 - defaulting such targets to the small buffer would
196 // defeat the whole point of the hotfix. Assume anyone not on classic AVR has plenty of RAM.
197 #if defined(RAMEND) && defined(RAMSTART)
198 #if ((RAMEND - RAMSTART) > 3000)
199 static const int crossingPointBufferSize = 100;
200 #else
201 static const int crossingPointBufferSize = 25;
202 #endif
203 #else
204 static const int crossingPointBufferSize = 100;
205 #endif
206
207 crossingPointBufferEntry crossingPointBuffer[crossingPointBufferSize];
208 int crossingPointBufferIndex = 0;
209 bool crossingPointBufferFull = false;
210};
211
212#endif
static unsigned long timeSinceMidnightDelta(unsigned long startMs, unsigned long endMs)
Elapsed milliseconds between two milliseconds-since-midnight timestamps.
Definition CrossingEngine.h:59
#define DOVES_MILLIS_PER_DAY
Definition CrossingEngine.h:36
LineDetectResult
Definition CrossingEngine.h:67
@ LINE_DETECT_IN_ZONE
Definition CrossingEngine.h:69
@ LINE_DETECT_NONE
Definition CrossingEngine.h:68
@ LINE_DETECT_COMPLETED
Definition CrossingEngine.h:70
Definition CrossingEngine.h:81
void setThreshold(double meters)
Sets the crossing zone threshold, in meters.
Definition CrossingEngine.cpp:21
void setDebugSerial(Stream *debugSerial)
Attaches / detaches a debug output stream.
Definition CrossingEngine.cpp:25
unsigned int getRejectedCrossingCount() const
Number of crossing-zone exits whose interpolation was rejected.
Definition CrossingEngine.cpp:37
void setForceLinear(bool linear)
Selects linear (true, default) or Catmull-Rom (false) position interpolation. Time and odometer are a...
Definition CrossingEngine.cpp:29
LineDetectResult detect(double currentLat, double currentLng, unsigned long currentTimeMs, float currentOdometer, float currentSpeedKmh, const crossingPointBufferEntry *prevFix, double pointALat, double pointALng, double pointBLat, double pointBLng, bool &crossingFlag, int lineLabel, double &outLat, double &outLng, unsigned long &outTime, double &outOdometer)
The crossing-zone state machine (formerly DovesLapTimer::_detectLineCrossing). Detects entry,...
Definition CrossingEngine.cpp:61
void reset()
Clears the buffer, the rejected-crossing counter, and any in-flight zone state. Does NOT clear the co...
Definition CrossingEngine.cpp:41
bool getForceLinear() const
Definition CrossingEngine.cpp:33
Definition CrossingEngine.h:73
double lng
Definition CrossingEngine.h:75
float speedKmh
Definition CrossingEngine.h:78
unsigned long time
Definition CrossingEngine.h:76
float odometer
Definition CrossingEngine.h:77
double lat
Definition CrossingEngine.h:74