DovesLapTimer 4.3.0
GPS-based lap timing Arduino library — go-karts to race cars
Loading...
Searching...
No Matches
GeoMath.h
Go to the documentation of this file.
1
11#ifndef _GEOMATH_H
12#define _GEOMATH_H
13
14#include <math.h>
15#include <float.h>
16
17// M_PI is not part of the C/C++ standard — it's a POSIX/BSD extension that
18// glibc happens to expose by default but stricter libcs (e.g. under
19// -std=c++NN / __STRICT_ANSI__) hide. Define a fallback so the library
20// compiles everywhere.
21#ifndef M_PI
22#define M_PI 3.14159265358979323846
23#endif
24
25static const double GEOMATH_RADIUS_EARTH = 6371.0 * 1000; // meters
26
27// Unit conversion constants — the single source of truth for the unit
28// boundaries the public API spans (knots from NMEA, km/h internally, mph
29// for the US-facing speed gates, feet for course lengths). One nautical
30// mile is exactly 1852 m and one statute mile exactly 1609.344 m, so the
31// derived factors below stay mutually consistent (previously three
32// hand-typed literals disagreed at the 7th digit).
33static constexpr double GEOMATH_KNOTS_TO_KMH = 1.852;
34static constexpr double GEOMATH_MPH_TO_KMH = 1.609344;
35static constexpr double GEOMATH_KMH_TO_MPH = 1.0 / GEOMATH_MPH_TO_KMH;
36static constexpr double GEOMATH_METERS_TO_FEET = 1.0 / 0.3048; // 1 ft = 0.3048 m exactly
37
47static inline bool geoIsFinite(double v) {
48 return v == v && v <= DBL_MAX && v >= -DBL_MAX;
49}
50
63static inline bool geoCoordinatesValid(double lat, double lng) {
64 if (!geoIsFinite(lat) || !geoIsFinite(lng)) return false;
65 if (lat < -90.0 || lat > 90.0 || lng < -180.0 || lng > 180.0) return false;
66 if (lat == 0.0 && lng == 0.0) return false;
67 return true;
68}
69
70static inline double geoHaversine(double lat1, double lon1, double lat2, double lon2) {
71 double lat1Rad = lat1 * M_PI / 180.0;
72 double lon1Rad = lon1 * M_PI / 180.0;
73 double lat2Rad = lat2 * M_PI / 180.0;
74 double lon2Rad = lon2 * M_PI / 180.0;
75
76 double sinHalfDLat = sin((lat2Rad - lat1Rad) / 2);
77 double sinHalfDLon = sin((lon2Rad - lon1Rad) / 2);
78
79 double a = sinHalfDLat * sinHalfDLat + cos(lat1Rad) * cos(lat2Rad) * sinHalfDLon * sinHalfDLon;
80 double c = 2 * atan2(sqrt(a), sqrt(1 - a));
81
82 return GEOMATH_RADIUS_EARTH * c;
83}
84
85static inline double geoHaversine3D(double prevLat, double prevLng, double prevAlt, double currentLat, double currentLng, double currentAlt) {
86 double dist = geoHaversine(prevLat, prevLng, currentLat, currentLng);
87 double altDiff = currentAlt - prevAlt;
88 return sqrt(dist * dist + altDiff * altDiff);
89}
90
101static inline int geoPointOnSideOfLine(double driverLat, double driverLng, double pointALat, double pointALng, double pointBLat, double pointBLng) {
102 double lineDirectionX = pointBLat - pointALat;
103 double lineDirectionY = pointBLng - pointALng;
104 double driverToPointAX = driverLat - pointALat;
105 double driverToPointAY = driverLng - pointALng;
106
107 double crossProduct = lineDirectionX * driverToPointAY - lineDirectionY * driverToPointAX;
108
109 if (crossProduct > 0) {
110 return -1; // CROSSING_LINE_SIDE_A
111 } else if (crossProduct < 0) {
112 return 1; // CROSSING_LINE_SIDE_B
113 }
114 return 0; // CROSSING_LINE_SIDE_EXACT
115}
116
125static inline double geoPointLineSegmentDistance(double pointX, double pointY, double startX, double startY, double endX, double endY) {
126 double dx = endX - startX;
127 double dy = endY - startY;
128 double segmentLengthSquared = dx * dx + dy * dy;
129
130 // Epsilon comparison handles degenerate segments (start == end).
131 if (segmentLengthSquared < 1e-12) {
132 return geoHaversine(pointX, pointY, startX, startY);
133 }
134
135 double projectionScalar = ((pointX - startX) * dx + (pointY - startY) * dy) / segmentLengthSquared;
136
137 if (projectionScalar < 0.0) {
138 return geoHaversine(pointX, pointY, startX, startY);
139 } else if (projectionScalar > 1.0) {
140 return geoHaversine(pointX, pointY, endX, endY);
141 }
142
143 double projectedX = startX + projectionScalar * dx;
144 double projectedY = startY + projectionScalar * dy;
145 return geoHaversine(pointX, pointY, projectedX, projectedY);
146}
147
157static inline bool geoInsideLineThreshold(double thresholdMeters, double driverLat, double driverLon, double crossingPointALat, double crossingPointALon, double crossingPointBLat, double crossingPointBLon) {
158 double driverLengthA = geoHaversine(driverLat, driverLon, crossingPointALat, crossingPointALon);
159 double driverLengthB = geoHaversine(driverLat, driverLon, crossingPointBLat, crossingPointBLon);
160 double crossingLineLength = geoHaversine(crossingPointALat, crossingPointALon, crossingPointBLat, crossingPointBLon);
161 double maxLineLength = sqrt(thresholdMeters * thresholdMeters + crossingLineLength * crossingLineLength);
162 return driverLengthA < maxLineLength && driverLengthB < maxLineLength;
163}
164
165#endif
static double geoPointLineSegmentDistance(double pointX, double pointY, double startX, double startY, double endX, double endY)
Shortest distance from a point to a line segment, in meters.
Definition GeoMath.h:125
static constexpr double GEOMATH_MPH_TO_KMH
Definition GeoMath.h:34
static constexpr double GEOMATH_KMH_TO_MPH
Definition GeoMath.h:35
static bool geoCoordinatesValid(double lat, double lng)
Validates a GPS coordinate pair before it is allowed to touch timing state.
Definition GeoMath.h:63
static bool geoIsFinite(double v)
Checks a value is a real, finite number (not NaN, not +/-infinity).
Definition GeoMath.h:47
static int geoPointOnSideOfLine(double driverLat, double driverLng, double pointALat, double pointALng, double pointBLat, double pointBLng)
Determines which side of an (infinite) line a point is on.
Definition GeoMath.h:101
static double geoHaversine(double lat1, double lon1, double lat2, double lon2)
Definition GeoMath.h:70
static bool geoInsideLineThreshold(double thresholdMeters, double driverLat, double driverLon, double crossingPointALat, double crossingPointALon, double crossingPointBLat, double crossingPointBLon)
Hypotenuse-based crossing-zone membership test.
Definition GeoMath.h:157
static double geoHaversine3D(double prevLat, double prevLng, double prevAlt, double currentLat, double currentLng, double currentAlt)
Definition GeoMath.h:85
static constexpr double GEOMATH_KNOTS_TO_KMH
Definition GeoMath.h:33
static const double GEOMATH_RADIUS_EARTH
Definition GeoMath.h:25
static constexpr double GEOMATH_METERS_TO_FEET
Definition GeoMath.h:36
#define M_PI
Definition GeoMath.h:22
const double crossingPointBLat
Definition basic_oled_example.ino:21
const double crossingPointALat
Definition basic_oled_example.ino:19