Docs: repeat-detection findings and interval backlog notes
This commit is contained in:
parent
db62d52650
commit
d6139a99f7
1 changed files with 296 additions and 0 deletions
|
|
@ -351,3 +351,299 @@ Aeropod). Трубка, направленная вперёд, плюс датч
|
|||
Готовый код обоих методов варианта 1 -- в `tools/track/track.py`
|
||||
(функция `wind_by_passes` и секция «ветер» в `report`), выше описан
|
||||
алгоритмически.
|
||||
|
||||
---
|
||||
|
||||
# БЭКЛОГ: автоматический разбор повторов внутри одной поездки
|
||||
|
||||
Заказано пользователем 17 сентября 2026.
|
||||
|
||||
## Задача
|
||||
|
||||
Пользователь катает интервалы: приезжает к подъёму, проезжает его 5 раз,
|
||||
скатываясь вниз между заездами, потом уезжает домой. Сейчас, чтобы разобрать
|
||||
такую тренировку, приходится либо останавливать и заново стартовать трек
|
||||
на каждом повторе (пользователь называет это бредом), либо разбирать
|
||||
вручную задним числом.
|
||||
|
||||
Нужно: **один трек на всю тренировку, повторы находятся сами.** Пользователь
|
||||
готов на отдельный режим «велотренировка», если так проще.
|
||||
|
||||
## Что должно получиться на выходе
|
||||
|
||||
Таблица повторов: номер, время, средняя скорость, каденс, мощность.
|
||||
И главное число, по которому принимается решение о завтрашнем дне:
|
||||
|
||||
отношение последнего повтора к первому
|
||||
|
||||
Порог, выведенный из данных (11 и 17 сентября, по 5 повторов):
|
||||
- последний быстрее или равен первому -- не на пределе, можно повторить завтра
|
||||
- последний медленнее на 5-10% -- нормальная тяжёлая тренировка
|
||||
- последний медленнее больше чем на 10% -- два дня спокойно
|
||||
|
||||
## Алгоритм, проверен вручную на этих данных
|
||||
|
||||
Повторы ищутся по самоподобию трека, без опорных точек от пользователя.
|
||||
|
||||
1. **Разложить точки поездки по ячейкам** ~10 м (сетка как в `heightmap.py`).
|
||||
2. **Найти ячейки, посещённые больше одного раза**, и отбросить те, где
|
||||
направление движения (`bearing_deg`) при посещениях различается больше
|
||||
чем на 60 градусов: проезд вниз по той же дороге повтором не считается.
|
||||
**Без проверки направления половина повторов теряется или считается
|
||||
задом наперёд** -- проверено, это главная ловушка.
|
||||
3. **Собрать самую длинную цепочку** таких ячеек, идущую подряд по ходу
|
||||
движения. Это и есть повторяемый участок. Требовать длину не меньше
|
||||
300 м и не меньше 3 проходов, иначе это перекрёсток у дома, а не подъём.
|
||||
4. **Разметить проходы:** для каждого прохода записать вход и выход
|
||||
по цепочке, время, длину.
|
||||
5. **Нормировать время на общую длину:** у пользователя GPS даёт от 1138
|
||||
до 1199 м на одном и том же подъёме, то есть 5% разброса. Без нормировки
|
||||
сравнение повторов между собой врёт.
|
||||
|
||||
## Чего делать НЕ надо
|
||||
|
||||
**Не просить пользователя ставить метки.** Он специально сказал, что
|
||||
останавливать и перезапускать трек не будет.
|
||||
|
||||
**Не опираться на высоту отдельного проезда.** Профиль повторяемого участка
|
||||
брать из карты высот по многим проездам (см. соседний раздел): у одиночного
|
||||
проезда GPS даёт ступеньки вида 0, 0, 0, 0, 8.5 м на ровном месте.
|
||||
|
||||
**Не считать мощность усреднением по точкам.** Усреднять по времени.
|
||||
При усреднении по точкам отрезки чуть разной длины дают разный вес
|
||||
медленным кускам, и на одинаковой скорости выходят разные ватты
|
||||
(проверено: 17.9 км/ч -> 173 Вт и 18.1 км/ч -> 209 Вт на одном подъёме).
|
||||
|
||||
## Готовый код
|
||||
|
||||
Ручная версия детектора по двум опорным точкам -- в истории этой сессии,
|
||||
в `tools/track/`. Она находит проходы правильно, но опорные точки задаются
|
||||
руками. Автоматизировать надо шаги 1-3.
|
||||
|
||||
---
|
||||
|
||||
# БЭКЛОГ: массовое уточнение высот по всем трекам
|
||||
|
||||
Заказано пользователем 17 сентября 2026. Приоритет свой, не срочный.
|
||||
|
||||
## Что нужно
|
||||
|
||||
Пункт меню, который уточняет высоты **пакетно**: идёт от последнего трека
|
||||
назад по времени и обрабатывает те, где `altitude_srtm_m` ещё не заполнена.
|
||||
Пользователь запускает его вручную, когда сидит на проводном интернете.
|
||||
|
||||
Требования к исполнению, из общих правил проекта:
|
||||
- показывать прогресс числами: сколько треков обработано, сколько осталось
|
||||
- прерываемость: в поле связь рвут РЭБ и РКН, процесс должен переживать
|
||||
обрыв и продолжаться с того места, где встал, а не начинать заново
|
||||
- уже уточнённые треки не трогать повторно
|
||||
|
||||
## Зачем, с измерениями
|
||||
|
||||
Аудит всех 83 велопоездок показал, что **высота за май и июнь непригодна**:
|
||||
|
||||
май-июнь: набор в БД 0-23 м, расчётный по сглаженной высоте 50-207 м
|
||||
июль-сентябрь: набор в БД и расчётный сходятся (168/138, 251/275, 227/235)
|
||||
|
||||
До 17 мая запись шла раз в 3-4 секунды при шуме высотомера 0.8 м между
|
||||
соседними точками. Пример 10 мая (session_id 3): сырая высота скачет
|
||||
`36 20 23 20 20 36 36 35 35 35 34 34 33 20 23 20 19 32 32 32 31`, то есть
|
||||
приёмник бегает между двумя уровнями с размахом 16 м. Медианный фильтр
|
||||
и скользящее среднее это приглаживают, но остаток даёт уклоны до 12.8%
|
||||
на треке, где реального набора 14 м на 13 км.
|
||||
|
||||
Практическое следствие: **любой анализ по уклонам за май и июнь даёт
|
||||
фальшивые результаты**, причём правдоподобные на вид. Построенная по этим
|
||||
данным кривая «лучший удержанный момент на шатунах» показала май лучшим
|
||||
месяцем сезона, хотя по мощности за 20 минут рост с мая по сентябрь
|
||||
составил 1.12 -> 1.75 Вт/кг.
|
||||
|
||||
Отдельно найдены отказы высотомера внутри годных треков: 2 сентября
|
||||
(session_id 1788361994788) после точки 4194 значение падает на 10 м
|
||||
и **замерзает** -- 26.8, 26.9, 26.9, 26.9 шестьдесят точек подряд,
|
||||
а после перерыва записи прыгает на 64.8 м с точностью 22 м. Уклоны
|
||||
на этом участке выдуманы целиком.
|
||||
|
||||
## Признаки негодной высоты, годятся как автотест
|
||||
|
||||
Все три считаются по уже записанным данным, без сети:
|
||||
|
||||
1. `elevation_gain_m` в сессии и набор, пересчитанный по точкам, расходятся
|
||||
больше чем вдвое в любую сторону
|
||||
2. медиана модуля разности высоты между соседними точками выше 1.5 м
|
||||
3. доля точек с шагом записи больше 8 секунд выше 15%
|
||||
|
||||
По этим признакам из 83 поездок отсеиваются 31. После уточнения высот
|
||||
через SRTM проверку надо прогнать повторно: она же покажет, помогло ли.
|
||||
|
||||
## Объём работы, посчитан по базе
|
||||
|
||||
всего точек 418 465
|
||||
уже уточнено 47 420 (11.3%, 12 сессий из 170)
|
||||
осталось 371 045
|
||||
запросов по 100 3 711
|
||||
|
||||
Размер пачки 100 задан в `frontend/js/main.js` около строки 3085 и совпадает
|
||||
с потолком OpenTopoData.
|
||||
|
||||
## Что надо поправить ДО массового прогона
|
||||
|
||||
**Откат по цепочке источников менять не надо.** Он сделан намеренно:
|
||||
лучше получить хоть какую-то высоту, чем никакой. Проблема не в откате,
|
||||
а в том, что после него не остаётся следа, кто именно ответил.
|
||||
|
||||
**1. Нет колонки источника.** В `gps_points` есть `altitude_srtm_m`, но нет
|
||||
поля, откуда это значение пришло. При работающем откате база неизбежно
|
||||
становится смесью источников, и без этой колонки отличить их потом нельзя
|
||||
никак. Нужна колонка (ALTER TABLE, без пересоздания), заполняемая при записи.
|
||||
Это единственное обязательное изменение.
|
||||
|
||||
**2. Откат должен быть виден пользователю.** Сейчас он сообщается только
|
||||
строкой в консоли (`[gpxz] http 403`). Достаточно показать в конце итог
|
||||
вида «уточнено 2314 точек: GPXZ 0, open-elevation 0, open-meteo 2314».
|
||||
|
||||
**3. Обработка 429 и 402 отдельно от сетевой ошибки.** По лимиту
|
||||
останавливаться до завтра, по обрыву повторять. Иначе при массовом прогоне
|
||||
упёршийся лимит молча отправит весь остаток базы к запасному источнику.
|
||||
|
||||
## Чего ждать и чего не ждать
|
||||
|
||||
Copernicus GLO-30 это сетка 30 м. На подъём длиной 200-400 м приходится
|
||||
7-13 отсчётов, этого хватает. На короткий рамп 60-100 м приходится 2-3
|
||||
отсчёта, форму по ним не восстановить. То есть массовое уточнение закроет
|
||||
затяжные подъёмы и общий набор, но НЕ закроет короткие рампы, на которых
|
||||
пользователь срывается. Для них нужен либо барометр, либо разовый замер
|
||||
профиля конкретного подъёма угломером.
|
||||
|
||||
## Лимиты GPXZ против объёма
|
||||
|
||||
Тарифы с их сайта: Evaluation бесплатно и 100 запросов в сутки (помечен
|
||||
«testing only»), Small 99 USD/мес и 2500 в сутки, Large 249 USD/мес и 7500.
|
||||
|
||||
3711 запросов при 100/сутки = 37 дней
|
||||
3711 запросов при 2500/сутки = 1.5 дня
|
||||
|
||||
Отдельно: одна поездка это медиана 19 запросов, но максимум 133. То есть
|
||||
**длинная поездка не влезает в бесплатный суточный лимит даже одна.**
|
||||
Если массовый прогон делать на бесплатном ключе, он обязан быть
|
||||
возобновляемым и считать свой суточный расход сам.
|
||||
|
||||
## Вариант без лимитов: свой Open Topo Data
|
||||
|
||||
GPXZ на своём сайте прямо пишет, что открытый форк их API-сервера
|
||||
называется Open Topo Data. Размер пачки 100, уже зашитый в коде,
|
||||
взят как раз из его потолка, то есть **протокол совместим и менять
|
||||
в клиенте нужен только базовый URL** (сейчас `https://api.gpxz.io/v1/...`
|
||||
захардкожен в `frontend/js/main.js` около строки 3091).
|
||||
|
||||
Считано по базе: все треки пользователя укрываются **двумя градусными
|
||||
тайлами 1x1**. Для GLO-30 это порядка сотни мегабайт на диске, разово
|
||||
скачивается по проводу.
|
||||
|
||||
Что это даёт против платного тарифа:
|
||||
- лимита нет вообще, весь бэклог прогоняется за один вечер
|
||||
- в поле сеть не нужна: у пользователя её глушат, и это постоянная
|
||||
проблема всех сетевых зависимостей приложения
|
||||
- тот же софт и тот же набор данных, значит результаты сопоставимы
|
||||
с уже уточнёнными 12 сессиями, если там был GPXZ
|
||||
|
||||
Минус: нужен хост, на котором это крутится, и разовая возня с датасетом.
|
||||
|
||||
## Но своего хоста может и не понадобиться
|
||||
|
||||
Автор Open Topo Data держит бесплатный публичный инстанс
|
||||
`api.opentopodata.org`. Его ограничения:
|
||||
|
||||
100 точек на запрос -- ровно тот размер пачки, что уже в коде
|
||||
1 запрос в секунду -- нужен троттлинг, сейчас пауз между пачками нет
|
||||
1000 запросов в сутки -- в десять раз больше бесплатного GPXZ
|
||||
|
||||
весь бэклог 3711 запросов -> 3.7 суток, 62 минуты чистого времени,
|
||||
то есть ~15 минут работы в день
|
||||
|
||||
Сравнение по срокам на тот же объём:
|
||||
|
||||
GPXZ Evaluation (бесплатно) 37.1 дней
|
||||
Open Topo Data публичный 3.7 дней
|
||||
GPXZ Small (99 USD/мес) 1.5 дня
|
||||
|
||||
**Ключ не нужен вообще**, значит отпадает и вопрос, укладывается ли
|
||||
массовый прогон личного архива в пометку «testing only» бесплатного
|
||||
тарифа GPXZ.
|
||||
|
||||
### Выбор датасета, и почему это не мелочь
|
||||
|
||||
На публичном инстансе **нет Copernicus GLO-30**, на котором работает GPXZ.
|
||||
Из глобальных 30-метровых доступны `mapzen`, `aster30m`, `srtm30m`.
|
||||
`eudem25m` (25 м, лучшая точность) покрывает только страны EEA, регион
|
||||
пользователя в его extent не входит.
|
||||
|
||||
Отсюда два следствия:
|
||||
|
||||
1. Датасет будет **не тот**, которым уточнены нынешние 12 сессий.
|
||||
Смешивать нельзя, и колонка источника из пункта 2 выше становится
|
||||
не пожеланием, а обязательной: без неё база превратится в винегрет
|
||||
из трёх рельефов.
|
||||
2. Разумный выбор по умолчанию `srtm30m` (USGS): у `aster30m` известны
|
||||
шумы и артефакты, `mapzen` это композит из нескольких источников,
|
||||
то есть внутри одного датасета точность неоднородна.
|
||||
|
||||
Хорошая проверка после прогона: 12 уже уточнённых сессий уточнить заново
|
||||
новым датасетом в отдельную колонку и сравнить набор высоты. Разойдутся
|
||||
сильно - значит выбор датасета важнее, чем кажется, и это надо знать
|
||||
до того, как на этих высотах строить разбор поездок.
|
||||
|
||||
## ПРОВЕРЕНО: в базе лежит не то, что все думали
|
||||
|
||||
Проверка сделана, и она важнее всего остального в этом разделе.
|
||||
|
||||
**Ключ GPXZ не работает и, судя по всему, не работал никогда.** Запрос
|
||||
к `api.gpxz.io` возвращает HTTP 403 с телом `error code: 1010`
|
||||
и заголовком `Server: cloudflare`. Ответ **одинаковый с ключом и без
|
||||
ключа**, то есть запрос режет Cloudflare по подписи клиента, до всякой
|
||||
проверки ключа. Ключ при этом валидный на вид: 28 символов, префикс `ak_`.
|
||||
|
||||
**Те 12 «уточнённых» сессий уточнены open-meteo.** Это третий, последний
|
||||
запасной источник в цепочке `GPXZ -> open-elevation -> open-meteo`.
|
||||
Доказательство: опорные целые значения в `altitude_srtm_m` совпадают
|
||||
с ответом open-meteo точка в точку и расходятся с `srtm30m` на 2-3 м.
|
||||
|
||||
**Дробные значения в базе создаёт приложение, а не источник.** Записанная
|
||||
последовательность `39.000, 38.857, 38.714, 38.571, 38.429, 38.286,
|
||||
38.143, 38.000` это `39 - k/7`, линейная интерполяция между двумя целыми
|
||||
отсчётами. Все внешние DEM (srtm30m, aster30m, mapzen, srtm90m,
|
||||
open-elevation, open-meteo) отдают **целые метры**. То есть дроби
|
||||
в колонке выглядят как высокая точность, а на деле это прямая, натянутая
|
||||
между узлами сетки в 30-90 м.
|
||||
|
||||
Практическое следствие для любого будущего разбора: **нельзя считать
|
||||
уклон по `altitude_srtm_m` на коротких кусках.** Интерполяция даёт
|
||||
гладкий правдоподобный профиль там, где данных нет вообще.
|
||||
|
||||
### Что из этого следует для задачи
|
||||
|
||||
1. Тихий откат (пункт 1 выше) не гипотетическая опасность, а уже
|
||||
случившийся факт: база заполнена последним источником в цепочке,
|
||||
и об этом никто не знал.
|
||||
2. Колонка источника (пункт 2) обязательна.
|
||||
3. Отказ надо показывать пользователю, а не писать в консоль.
|
||||
|
||||
### Покупать ли GPXZ: нет
|
||||
|
||||
С карты покрытия на их сайте: лидар 0.5-2 м у GPXZ есть только в США,
|
||||
западной и северной Европе и Новой Зеландии. Регион пользователя
|
||||
в жёлтой зоне, где по их же легенде 30 м, а по их тексту база
|
||||
из Copernicus 30m. То есть платный GPXZ дал бы Copernicus 30m вместо
|
||||
бесплатного SRTM 30m: другая вертикальная точность, **та же
|
||||
горизонтальная сетка 30 м**.
|
||||
|
||||
А упирается всё именно в горизонтальную: подъём длиной 60-100 м, на
|
||||
котором пользователь срывается, это 2-3 отсчёта на любой тридцатиметровой
|
||||
сетке. Ни один DEM его не разрешит, ни платный, ни бесплатный.
|
||||
|
||||
Измерено на одном подъёме (1153 м, 229 точек, первый заезд 16 сентября):
|
||||
источники расходятся между собой до 2-3% уклона на сотню метров, что
|
||||
того же порядка, что шум GPS. `mapzen` при этом отдаёт числа, побитово
|
||||
совпадающие с `srtm30m`.
|
||||
|
||||
Вывод: брать бесплатный `srtm30m` с публичного Open Topo Data.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue