Repeat analysis: pick the longest corridor traversal as reference (fix short segment)
This commit is contained in:
parent
b2defb4564
commit
0d8b5f97b9
2 changed files with 44 additions and 19 deletions
|
|
@ -43,6 +43,20 @@
|
|||
совпадает с харнессом, вердикт зелёный 105%, участок подсвечен бирюзой на карте. Скрин.
|
||||
- Далее: сборка APK + проверка на телефоне (реальные интервалы там же).
|
||||
|
||||
## Фикс после проверки на теле (трек 16 сен)
|
||||
- Симптом (пользователь): на 16 сен в повторы попала только часть горки, 729м вместо 1200+,
|
||||
на карте видно прямую, что идёт дальше.
|
||||
- Причина: эталон брался с ПЕРВОЙ горячей ячейки. Поездка 16 сен стартует прямо в горячей
|
||||
ячейке (подъезд частично совпал с коридором), проход с индекса 0 обрывался гэпом на 143й
|
||||
точке -> 729м, хотя горячих ячеек ~183 (~1830м).
|
||||
- Фикс: эталон = САМЫЙ ДЛИННЫЙ чистый проход (перебор всех стартов горячих ранов,
|
||||
`buildTraversal`), не первый. Дёшево (стартов немного).
|
||||
- Проверка: харнесс 16 сен -> участок 1572м (было 729), 3 полных заезда. 11/17 сен без
|
||||
изменений (по 5). Контроль (5 обычных) - 0 ложных. Десктоп (Xvfb, подсажена 16 сен,
|
||||
бэкап+удаление): подсветка на карте покрывает весь верхний коридор, таблица 1.57км/3 заезда,
|
||||
вердикт 104%. По коридору 4 траверса, но первый 74% (начат выше низа) - отсечён правилом
|
||||
80% (как pers.py MIN_FRAC), иначе нормировка длина/время завысила бы его скорость.
|
||||
|
||||
## Не в этой задаче
|
||||
Из того же файла НЕ делалось: поправка модели п.2 (аддитивная мощность вместо max(физика,MET);
|
||||
п.1 про Crr уже был сделан ранее), массовое уточнение высот, ветер (Часть 2 - там вопросы).
|
||||
|
|
|
|||
|
|
@ -130,26 +130,37 @@ GPS.detectRepeats = function(points, opts) {
|
|||
}
|
||||
let isHot = (k) => k != null && (visits.get(k) || 0) >= minPasses;
|
||||
|
||||
// Reference segment = ONE clean traversal of the corridor: start at the first hot
|
||||
// cell, add each new hot cell in order, and STOP at the first revisit (that means the
|
||||
// rider turned back / started the next lap). Gluing several laps into the reference
|
||||
// (a long hot run) inflates its length and scrambles the order — measured, gives
|
||||
// absurd 9-second "passes" over a 4.4 km "segment". One traversal fixes both.
|
||||
let s0 = 0;
|
||||
while (s0 < n && !isHot(key[s0])) s0++;
|
||||
if (s0 >= n) return { found: false };
|
||||
let seq = [], order = new Map(), s1 = s0;
|
||||
for (let k = s0, gap = 0; k < n; k++) {
|
||||
let c = key[k];
|
||||
if (c == null || !isHot(c)) { gap++; if (gap > 5) break; continue; } // left the corridor
|
||||
gap = 0;
|
||||
if (seq.length && c === seq[seq.length - 1]) { s1 = k; continue; } // still in the same cell
|
||||
if (order.has(c)) break; // revisit of an earlier cell → first traversal complete
|
||||
order.set(c, seq.length); seq.push(c); s1 = k;
|
||||
// Reference segment = the LONGEST clean traversal of the corridor. One traversal =
|
||||
// start at a cell, add each NEW hot cell in order, STOP at the first revisit (turned
|
||||
// back / next lap) or a gap out of the corridor. Gluing laps together inflates length
|
||||
// and scrambles order (measured: absurd 9 s "passes" over 4.4 km); a single traversal
|
||||
// fixes that. But picking the FIRST hot cell is also wrong — a ride can START inside a
|
||||
// hot cell (its approach overlaps the corridor), giving a short gap-truncated seq
|
||||
// (measured: 729 m of a 1572 m climb). So try a traversal from every hot-run start and
|
||||
// keep the longest.
|
||||
let buildTraversal = (start) => {
|
||||
let seq = [], order = new Map(), end = start;
|
||||
for (let k = start, gap = 0; k < n; k++) {
|
||||
let c = key[k];
|
||||
if (c == null || !isHot(c)) { gap++; if (gap > 5) break; continue; } // left the corridor
|
||||
gap = 0;
|
||||
if (seq.length && c === seq[seq.length - 1]) { end = k; continue; } // still in the same cell
|
||||
if (order.has(c)) break; // revisit of an earlier cell → traversal complete
|
||||
order.set(c, seq.length); seq.push(c); end = k;
|
||||
}
|
||||
let len = 0;
|
||||
for (let k = start + 1; k <= end; k++)
|
||||
len += GPS.distance(points[k - 1].lat, points[k - 1].lon, points[k].lat, points[k].lon);
|
||||
return { seq, order, len };
|
||||
};
|
||||
let best = null;
|
||||
for (let k = 0; k < n; k++) {
|
||||
if (!isHot(key[k]) || (k > 0 && isHot(key[k - 1]))) continue; // only at a hot-run start
|
||||
let t = buildTraversal(k);
|
||||
if (!best || t.len > best.len) best = t;
|
||||
}
|
||||
let segLength = 0;
|
||||
for (let k = s0 + 1; k <= s1; k++)
|
||||
segLength += GPS.distance(points[k - 1].lat, points[k - 1].lon, points[k].lat, points[k].lon);
|
||||
if (!best) return { found: false };
|
||||
let seq = best.seq, order = best.order, segLength = best.len;
|
||||
if (seq.length < 3 || segLength < minLenM) return { found: false };
|
||||
|
||||
// Passes: scan the ride; a pass is a run where position along seq rises from the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue