Skip to content

Commit c71c48d

Browse files
committed
test(nav): Testing navigation widgets
1 parent 2cd9868 commit c71c48d

File tree

7 files changed

+143
-169
lines changed

7 files changed

+143
-169
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,8 @@ To access the Web File Server, simply use any browser and go to the following ad
337337
- [ ] Multiple IMU's and Compass module implementation
338338
- [X] Power saving
339339
- [X] Vector maps
340-
- [ ] Google Maps navigation style (turn by turn)
341-
- [x] Optimize code
340+
- [X] Google Maps navigation style (turn by turn)
341+
- [X] Optimize code
342342
- [X] Fix bugs!
343343
- [X] Web file server
344344

lib/gpx/src/gpxParser.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,42 @@ std::vector<TurnPoint> GPXParser::getTurnPoints(float thresholdDeg, double minDi
457457

458458
return turnPoints;
459459
}
460+
461+
462+
std::vector<TurnPoint> GPXParser::getTurnPointsSlidingWindow(
463+
float thresholdDeg, double minDist, float sharpTurnDeg,
464+
int windowSize, const std::vector<wayPoint>& trackData)
465+
{
466+
std::vector<TurnPoint> turnPoints;
467+
double accumDist = 0;
468+
if (trackData.size() < 2 * windowSize + 1)
469+
return turnPoints;
470+
471+
for (size_t i = windowSize; i < trackData.size() - windowSize; ++i)
472+
{
473+
// Distancia total de la ventana (puedes usarla como filtro si quieres)
474+
double distWindow = 0;
475+
for (int j = int(i - windowSize); j < int(i + windowSize); ++j)
476+
distWindow += calcDist(trackData[j].lat, trackData[j].lon, trackData[j+1].lat, trackData[j+1].lon);
477+
478+
// Ángulo global entre el tramo inicial y final de la ventana
479+
double brgStart = calcCourse(trackData[i - windowSize].lat, trackData[i - windowSize].lon,
480+
trackData[i].lat, trackData[i].lon);
481+
double brgEnd = calcCourse(trackData[i].lat, trackData[i].lon,
482+
trackData[i + windowSize].lat, trackData[i + windowSize].lon);
483+
double diff = calcAngleDiff(brgEnd, brgStart);
484+
485+
accumDist += calcDist(trackData[i-1].lat, trackData[i-1].lon, trackData[i].lat, trackData[i].lon);
486+
487+
// Detección igual que el método clásico, pero sobre el ángulo "global" de la ventana
488+
if (std::abs(diff) > sharpTurnDeg) {
489+
turnPoints.push_back({static_cast<int>(i), diff, accumDist});
490+
continue;
491+
}
492+
if (distWindow < minDist)
493+
continue;
494+
if (std::abs(diff) > thresholdDeg)
495+
turnPoints.push_back({static_cast<int>(i), diff, accumDist});
496+
}
497+
return turnPoints;
498+
}

lib/gpx/src/gpxParser.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class GPXParser
6262
bool addWaypoint(const wayPoint& wp);
6363
bool loadTrack(std::vector<wayPoint>& trackData);
6464
std::vector<TurnPoint> getTurnPoints(float thresholdDeg, double minDist, float sharpTurnDeg, std::vector<wayPoint>& trackData);
65+
std::vector<TurnPoint> getTurnPointsSlidingWindow(float thresholdDeg, double minDist, float sharpTurnDeg,int windowSize, const std::vector<wayPoint>& trackData);
6566

6667
std::string filePath;
6768
};

lib/gui/src/gpxScr.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,10 @@ void gpxListEvent(lv_event_t *event)
8585
if (gpxTrack)
8686
{
8787
gpx.loadTrack(trackData);
88-
turnPoints = gpx.getTurnPoints(18.0f, 20, 70.0f, trackData);
88+
// turnPoints = gpx.getTurnPoints(18.0f, 20, 70.0f, trackData);
89+
turnPoints = gpx.getTurnPointsSlidingWindow(18.0f, 20, 70.0f, 3, trackData);
8990
isTrackLoaded = true;
9091
lv_obj_clear_flag(turnByTurn,LV_OBJ_FLAG_HIDDEN);
91-
92-
for (auto& tp : turnPoints)
93-
{
94-
ESP_LOGI(TAG, "idx: %d, angle: %.1f°, dist: %.1fm", tp.idx, tp.angle, tp.distance);
95-
}
9692
mapView.updateMap();
9793
lv_obj_send_event(mapTile, LV_EVENT_REFRESH, NULL);
9894
}

lib/utils/src/navigation.cpp

Lines changed: 88 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,19 @@
66
* @date 2025-06
77
*/
88

9-
#include "navigation.hpp"
9+
#include "navigation.hpp"
10+
11+
extern lv_obj_t *turnDistLabel;
12+
extern lv_obj_t *turnImg;
13+
14+
LV_IMG_DECLARE(straight);
15+
LV_IMG_DECLARE(slleft);
16+
LV_IMG_DECLARE(slright);
17+
LV_IMG_DECLARE(tleft);
18+
LV_IMG_DECLARE(tright);
19+
LV_IMG_DECLARE(uleft);
20+
LV_IMG_DECLARE(uright);
21+
LV_IMG_DECLARE(finish);
1022

1123
/**
1224
* @brief Find the closest track point index to the user's current position.
@@ -42,29 +54,9 @@
4254
* @return The index of the closest point in the track within the search window or full track if needed.
4355
*/
4456

45-
4657
int findClosestTrackPoint(float userLat, float userLon, const std::vector<wayPoint>& track, int lastIdx)
4758
{
48-
// int window = 20;
49-
// int start = std::max(0, lastIdx - window);
50-
// int end = std::min((int)track.size() - 1, lastIdx + window);
51-
52-
// int closestIdx = lastIdx;
53-
// float minDist = calcDist(userLat, userLon, track[lastIdx].lat, track[lastIdx].lon);
54-
// for (int i = start; i <= end; ++i) {
55-
// float d = calcDist(userLat, userLon, track[i].lat, track[i].lon);
56-
// if (d < minDist) {
57-
// minDist = d;
58-
// closestIdx = i;
59-
// }
60-
// }
61-
// // Prevent small backward jumps on the track
62-
// if (closestIdx < lastIdx && (lastIdx - closestIdx) < 5) {
63-
// closestIdx = lastIdx;
64-
// }
65-
// return closestIdx;
66-
67-
int window = 20;
59+
int window = 50;
6860
int n = (int)track.size();
6961
int start = std::max(0, lastIdx - window);
7062
int end = std::min(n - 1, lastIdx + window);
@@ -74,14 +66,17 @@ int findClosestTrackPoint(float userLat, float userLon, const std::vector<wayPoi
7466
int closestIdx = lastIdx;
7567

7668
// Si la distancia al último punto es grande, busca en todo el track
77-
if (minDist > 2 * window) { // o un umbral en metros, ej: 50m
69+
if (minDist > 2 * window)
70+
{ // o un umbral en metros, ej: 50m
7871
start = 0;
7972
end = n - 1;
8073
}
8174

82-
for (int i = start; i <= end; ++i) {
75+
for (int i = start; i <= end; ++i)
76+
{
8377
float d = calcDist(userLat, userLon, track[i].lat, track[i].lon);
84-
if (d < minDist) {
78+
if (d < minDist)
79+
{
8580
minDist = d;
8681
closestIdx = i;
8782
}
@@ -93,174 +88,111 @@ int findClosestTrackPoint(float userLat, float userLon, const std::vector<wayPoi
9388
return closestIdx;
9489
}
9590

96-
/**
97-
* @brief Enhanced turn-by-turn navigation logic with "soft curve" detection for S-shaped and gentle bends.
98-
*
99-
* This function updates the navigation state and determines which instructions or alerts should be shown to the user.
100-
* - It supports off-track detection.
101-
* - The distance to the next turn is updated and shown dynamically ("live") on every cycle.
102-
* - Introduces a "soft curve" warning for angles between 15 and 60 degrees, so S-shaped and gentle curves are not interpreted as straight.
103-
* - The warning flags (warnedPreTurn, warnedTurn) are kept for possible unique actions (sound, vibration), but visual messages are refreshed every cycle.
91+
/**
92+
* @brief Navegación simplificada: muestra solo el siguiente evento (recto, curva suave o fuerte) y únicamente lo avisa cuando quedan warnDist metros o menos.
10493
*
105-
* @param userLat Current user latitude.
106-
* @param userLon Current user longitude.
107-
* @param userHeading Current heading of the user (degrees).
108-
* @param speed_kmh Current user speed in km/h.
109-
* @param track Vector of wayPoints representing the GPX track.
110-
* @param turns Vector of TurnPoint, each representing a detected turn on the track.
111-
* @param state Navigation state struct, updated persistently across function calls.
94+
* @param userLat Latitud del usuario.
95+
* @param userLon Longitud del usuario.
96+
* @param userHeading Rumbo del usuario (grados).
97+
* @param speed_kmh Velocidad del usuario (km/h).
98+
* @param track Vector de wayPoints del track GPX.
99+
* @param turns Vector de TurnPoint (giros detectados).
100+
* @param state Estado de navegación.
101+
* @param minAngleForCurve Umbral angular mínimo para considerar curva relevante (por defecto 15°).
102+
* @param warnDist Distancia (en metros) para mostrar el aviso del evento (por defecto 100).
112103
*/
113104
void updateNavigation(
114105
float userLat, float userLon, float userHeading, float speed_kmh,
115106
const std::vector<wayPoint>& track,
116107
const std::vector<TurnPoint>& turns,
117-
NavState& state
118-
) {
119-
float lookahead = 1000.0; // meters: how far ahead to consider upcoming turns
120-
float umbralSeguirRecto = 200.0; // meters to show "go straight" or "soft curve"
121-
float preWarnDist = speed_kmh > 11.0 ? 150.0 : 80.0; // meters for pre-turn warning
122-
float warnDist = 50.0; // meters for final turn warning
123-
float minAngleForCurve = 15.0; // degrees: minimum angle to consider as "curva suave"
108+
NavState& state,
109+
float minAngleForCurve,
110+
float warnDist
111+
)
112+
{
113+
const float minTurnDist = 5.0f; // Umbral para saltar turnpoints ya pasados o justo encima
124114

125115
int closestIdx = findClosestTrackPoint(userLat, userLon, track, state.lastTrackIdx);
126-
127116
float distToTrack = calcDist(userLat, userLon, track[closestIdx].lat, track[closestIdx].lon);
128117
if (distToTrack > 30.0)
129118
{
130-
// Pseudocode: show "off track" bitmap
131-
// mostrarBitmap(LVGL_BITMAP_FUERA_RUTA);
132119
log_i("¡Fuera de ruta! Reincorpórate al track");
133-
state.warnedStraight = false;
134-
state.warnedTurn = false;
135-
state.warnedPreTurn = false;
120+
state.lastTrackIdx = closestIdx;
136121
return;
137122
}
138123

139-
// --- Find the most relevant next turn within lookahead ---
140-
int relevantTurnIdx = -1;
141-
float distanceToRelevantTurn = std::numeric_limits<float>::max();
124+
// Avanza el índice de turnpoint si ya lo hemos pasado (por índice)
125+
while (state.nextTurnIdx < (int)turns.size() && turns[state.nextTurnIdx].idx <= closestIdx)
126+
state.nextTurnIdx++;
127+
128+
// Busca el siguiente evento relevante (el primero con distancia suficiente)
129+
int nextEventIdx = -1;
130+
float distanceToNextEvent = std::numeric_limits<float>::max();
142131
float abs_angle = 0.0;
143132
bool derecha = false;
144133

145-
// Scan all upcoming turns
146-
for (int i = state.nextTurnIdx; i < (int)turns.size(); ++i) {
134+
for (int i = state.nextTurnIdx; i < (int)turns.size(); ++i)
135+
{
147136
float turnLat = track[turns[i].idx].lat;
148137
float turnLon = track[turns[i].idx].lon;
149138
float distanceToTurn = calcDist(userLat, userLon, turnLat, turnLon);
150-
151-
if (distanceToTurn < distanceToRelevantTurn) {
152-
distanceToRelevantTurn = distanceToTurn;
153-
relevantTurnIdx = i;
154-
abs_angle = fabs(turns[i].angle);
155-
derecha = (turns[i].angle > 0);
156-
}
157-
// stop search if the next turn is outside the lookahead window
158-
if (distanceToTurn > lookahead) break;
139+
// Si el evento está “demasiado cerca” (o ya pasado), sáltalo y sigue buscando
140+
if (distanceToTurn < minTurnDist)
141+
continue;
142+
143+
distanceToNextEvent = distanceToTurn;
144+
nextEventIdx = i;
145+
abs_angle = fabs(turns[i].angle);
146+
derecha = (turns[i].angle > 0);
147+
break; // Encontrado el siguiente relevante, sal del bucle
159148
}
160149

161-
// If no relevant turn in lookahead: show recto to the next turn (even if it is far)
162-
if (relevantTurnIdx == -1) {
163-
if (!turns.empty()) {
164-
// Still show "seguir recto" to the very next turn, even if > lookahead
165-
float turnLat = track[turns[state.nextTurnIdx].idx].lat;
166-
float turnLon = track[turns[state.nextTurnIdx].idx].lon;
167-
float distanceToTurn = calcDist(userLat, userLon, turnLat, turnLon);
168-
log_i("Sigue recto durante %d m", (int)distanceToTurn);
169-
} else {
170-
log_i("Fin de ruta o sin más giros.");
171-
}
172-
state.warnedStraight = false;
173-
state.warnedTurn = false;
174-
state.warnedPreTurn = false;
175-
state.lastTrackIdx = closestIdx;
176-
return;
177-
}
178-
179-
// Update state.nextTurnIdx if we've passed a turn
180-
while (state.nextTurnIdx < (int)turns.size() && turns[state.nextTurnIdx].idx <= closestIdx)
181-
state.nextTurnIdx++;
182-
183-
// --- Nueva lógica de avisos para el giro relevante ---
184-
if (distanceToRelevantTurn > umbralSeguirRecto)
150+
// Si no queda ningún evento relevante
151+
if (nextEventIdx == -1)
185152
{
186-
if (abs_angle > minAngleForCurve && abs_angle < 60) {
187-
// Soft curve
188-
if (derecha)
189-
log_i("Curva suave a la DERECHA en %d m", (int)distanceToRelevantTurn);
190-
else
191-
log_i("Curva suave a la IZQUIERDA en %d m", (int)distanceToRelevantTurn);
192-
} else {
193-
// Go straight
194-
log_i("Sigue recto durante %d m", (int)distanceToRelevantTurn);
195-
}
196-
state.warnedStraight = true;
197-
state.warnedPreTurn = false;
198-
state.warnedTurn = false;
153+
lv_img_set_src(turnImg, &finish);
199154
state.lastTrackIdx = closestIdx;
200155
return;
201156
}
202157

203-
// --- Pre-turn warning: dynamically update distance ---
204-
if (distanceToRelevantTurn < preWarnDist && distanceToRelevantTurn > warnDist) {
205-
if (abs_angle < 60 && abs_angle >= minAngleForCurve) {
206-
if (derecha)
207-
log_i("Preaviso: curva suave a la DERECHA en %d m", (int)distanceToRelevantTurn);
208-
else
209-
log_i("Preaviso: curva suave a la IZQUIERDA en %d m", (int)distanceToRelevantTurn);
210-
} else if (abs_angle < 60) {
158+
// Mostrar solo cuando falten <= warnDist metros
159+
if (distanceToNextEvent <= warnDist)
160+
{
161+
if (abs_angle >= minAngleForCurve && abs_angle < 60)
162+
{
211163
if (derecha)
212-
log_i("Preaviso: giro leve a la DERECHA en %d m", (int)distanceToRelevantTurn);
164+
lv_img_set_src(turnImg, &slright);
213165
else
214-
log_i("Preaviso: giro leve a la IZQUIERDA en %d m", (int)distanceToRelevantTurn);
166+
lv_img_set_src(turnImg, &slleft);
215167
}
216-
else if (abs_angle < 120) {
168+
else if (abs_angle >= 60)
169+
{
217170
if (derecha)
218-
log_i("Preaviso: giro medio a la DERECHA en %d m", (int)distanceToRelevantTurn);
171+
lv_img_set_src(turnImg, &tright);
219172
else
220-
log_i("Preaviso: giro medio a la IZQUIERDA en %d m", (int)distanceToRelevantTurn);
173+
lv_img_set_src(turnImg, &tleft);
174+
221175
}
222-
else {
223-
if (derecha)
224-
log_i("Preaviso: giro cerrado a la DERECHA en %d m", (int)distanceToRelevantTurn);
225-
else
226-
log_i("Preaviso: giro cerrado a la IZQUIERDA en %d m", (int)distanceToRelevantTurn);
176+
// else if (abs_angle > 3.0)
177+
// {
178+
// log_i("Desvío leve a la %s en %d m (%.1f° en idx %d)",
179+
// derecha ? "DERECHA" : "IZQUIERDA",
180+
// (int)distanceToNextEvent,
181+
// abs_angle,
182+
// turns[nextEventIdx].idx
183+
// );
184+
// }
185+
else
186+
{
187+
lv_img_set_src(turnImg, &straight);
227188
}
228-
state.warnedPreTurn = true;
229-
state.warnedStraight = false;
230189
}
231-
// --- Final turn warning: dynamically update distance ---
232-
if (distanceToRelevantTurn <= warnDist) {
233-
if (abs_angle < 60 && abs_angle >= minAngleForCurve) {
234-
if (derecha)
235-
log_i("¡Curva suave a la DERECHA en %d m!", (int)distanceToRelevantTurn);
236-
else
237-
log_i("¡Curva suave a la IZQUIERDA en %d m!", (int)distanceToRelevantTurn);
238-
} else if (abs_angle < 60) {
239-
if (derecha)
240-
log_i("¡Giro leve a la DERECHA en %d m!", (int)distanceToRelevantTurn);
241-
else
242-
log_i("¡Giro leve a la IZQUIERDA en %d m!", (int)distanceToRelevantTurn);
243-
}
244-
else if (abs_angle < 120) {
245-
if (derecha)
246-
log_i("¡Giro medio a la DERECHA en %d m!", (int)distanceToRelevantTurn);
247-
else
248-
log_i("¡Giro medio a la IZQUIERDA en %d m!", (int)distanceToRelevantTurn);
249-
}
250-
else {
251-
if (derecha)
252-
log_i("¡Giro cerrado a la DERECHA en %d m!", (int)distanceToRelevantTurn);
253-
else
254-
log_i("¡Giro cerrado a la IZQUIERDA en %d m!", (int)distanceToRelevantTurn);
255-
}
256-
state.warnedTurn = true;
257-
state.warnedStraight = false;
258-
}
259-
// Reset warnings if moving away from turn
260-
if (distanceToRelevantTurn > preWarnDist)
190+
else
261191
{
262-
state.warnedTurn = false;
263-
state.warnedPreTurn = false;
192+
lv_img_set_src(turnImg, &straight);
264193
}
194+
195+
lv_label_set_text_fmt(turnDistLabel, "%4d", (int)distanceToNextEvent);
196+
265197
state.lastTrackIdx = closestIdx;
266198
}

0 commit comments

Comments
 (0)