Repeat analysis: bridge a stop inside a pass, count 70%+ passes (dropped-seat rep now counts)
This commit is contained in:
parent
1a57f36621
commit
7d2581dc2d
2 changed files with 35 additions and 7 deletions
|
|
@ -57,6 +57,22 @@
|
|||
вердикт 104%. По коридору 4 траверса, но первый 74% (начат выше низа) - отсечён правилом
|
||||
80% (как pers.py MIN_FRAC), иначе нормировка длина/время завысила бы его скорость.
|
||||
|
||||
## Фикс 2: остановка внутри заезда (седло) + сшивка (трек 16 сен)
|
||||
- Пользователь: первый заезд 16 сен был полным, но разорвался остановкой (опущенное седло,
|
||||
ноги не крутили) - такие стопы <~минуты сшивать в один заезд, время стопа оставлять в
|
||||
заезде как штраф.
|
||||
- Проверка данных: остановка ЕСТЬ - idx 310-319, **89 секунд** сдвиг <15м. По reported speed
|
||||
её не видно (fused показывал 8 км/ч), нашлась по СМЕЩЕНИЮ. Плюс у вершины трек первого
|
||||
заезда уходит с общей линии в негорячие ячейки (GPS-разброс на 20 км/ч), из-за чего покрытие
|
||||
вышло 74%.
|
||||
- Сделано: (1) в разметке проходов - мост через короткое прерывание рядом с коридором
|
||||
(`bridgeSec` 90с, `bridgeM` 60м) + сохранена терпимость к нулевым пропускам (gap<=25);
|
||||
время простоя остаётся в длительности заезда -> честный штраф скорости. (2) Порог покрытия
|
||||
`minFrac` 0.8 -> **0.7**: полный заезд, у которого GPS сполз с линии у вершины, засчитывается.
|
||||
- Проверка (харнесс): 16 сен -> **4 заезда** (был 3), норм.скор [19.6, 21.3, 21.2, 23.8] -
|
||||
первый самый медленный (штраф за 89с стоп), вердикт 121%. 11/17 сен без изменений (по 5),
|
||||
контроль 5 обычных - 0 ложных при 0.7. Десктоп: 16 сен подсажен, таблица 4 строки.
|
||||
|
||||
## Не в этой задаче
|
||||
Из того же файла НЕ делалось: поправка модели п.2 (аддитивная мощность вместо max(физика,MET);
|
||||
п.1 про Crr уже был сделан ранее), массовое уточнение высот, ветер (Часть 2 - там вопросы).
|
||||
|
|
|
|||
|
|
@ -106,7 +106,11 @@ GPS.detectRepeats = function(points, opts) {
|
|||
let cell = opts.cellM ?? 10; // grid cell ~10 m (as in tools/track heightmap)
|
||||
let minLenM = opts.minLenM ?? 300; // repeated segment must be at least this long
|
||||
let minPasses = opts.minPasses ?? 3; // and ridden at least this many times
|
||||
let minFrac = opts.minFrac ?? 0.8; // a pass must cover this fraction of the segment
|
||||
let minFrac = opts.minFrac ?? 0.7; // a pass must cover this fraction of the segment (0.7:
|
||||
// a full rep whose GPS drifted off the common line near
|
||||
// the top still counts; its stop time penalises its speed)
|
||||
let bridgeSec = opts.bridgeSec ?? 90; // bridge an interruption inside a pass up to this long
|
||||
let bridgeM = opts.bridgeM ?? 60; // ...as long as the rider stays within this of the corridor
|
||||
let n = points ? points.length : 0;
|
||||
if (n < 20) return { found: false };
|
||||
|
||||
|
|
@ -170,15 +174,23 @@ GPS.detectRepeats = function(points, opts) {
|
|||
let p = 0;
|
||||
while (p < n) {
|
||||
if (pos[p] == null || pos[p] > seq.length * 0.15) { p++; continue; }
|
||||
let q = p, lo = pos[p], hi = pos[p], gap = 0;
|
||||
let q = p, lo = pos[p], hi = pos[p], anchor = p, gap = 0;
|
||||
while (q < n - 1) {
|
||||
q++;
|
||||
if (pos[q] == null) { gap++; if (gap > 25) break; continue; }
|
||||
if (pos[q] < hi - 3) break; // went backward — pass ended (e.g. coasting down)
|
||||
gap = 0; hi = Math.max(hi, pos[q]);
|
||||
if (pos[q] != null && pos[q] >= hi - 3) { gap = 0; hi = Math.max(hi, pos[q]); anchor = q; continue; }
|
||||
// Off the corridor (a gap) or a step backward. BRIDGE a short interruption near the
|
||||
// corridor - a stop (dropped seat, mechanical) or GPS wobble/divergence - so it stays
|
||||
// ONE pass, with the idle time kept in the pass duration (a fair speed penalty). A real
|
||||
// descent between reps moves far and long, exceeding the bridge, and ends the pass.
|
||||
let dtStop = (new Date(points[q].ts) - new Date(points[anchor].ts)) / 1000;
|
||||
let dispStop = GPS.distance(points[anchor].lat, points[anchor].lon, points[q].lat, points[q].lon);
|
||||
if ((dtStop <= bridgeSec && dispStop <= bridgeM) || ++gap <= 25) continue;
|
||||
break;
|
||||
}
|
||||
if ((hi - lo) >= seq.length * minFrac) passes.push({ startIdx: p, endIdx: q });
|
||||
p = q + 1;
|
||||
// Pass spans [p, anchor] (last on-corridor point); duration ts[anchor]-ts[p] includes any
|
||||
// bridged stop, so a pass with a rest reads slower, exactly as intended.
|
||||
if ((hi - lo) >= seq.length * minFrac) passes.push({ startIdx: p, endIdx: anchor });
|
||||
p = Math.max(q, anchor) + 1;
|
||||
}
|
||||
if (passes.length < minPasses) return { found: false };
|
||||
return { found: true, segLength: Math.round(segLength), passCount: passes.length, passes };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue