48 KiB
Находки по расчётной модели GPS (2026-09-07)
Получено при независимом разборе базы baldav.db (156 поездок, 366 тыс. точек)
сторонним инструментом tools/track/. Константы там взяты из GPS.computeStats,
поэтому расхождения означают разницу в модели, а не в настройках.
Проверка сходимости: дистанция расходится с базой на 0.2%, калории по формуле приложения -- на 4% (трек 4 сентября). То есть база и формула воспроизводятся, и нижеперечисленное -- не ошибки чтения.
Пункты про нулевую отметку высоты и паузу на подъёме сюда не включены: они уже обсуждены отдельно.
Файл состоит из двух частей. Первая -- находки: измеренные расхождения, каждое с замером и по возможности с контрольной проверкой. Вторая -- предположения: варианты отслеживания ветра, составленные по просьбе пользователя, с вопросами к вам в конце. Смешивать их не стоит: первая часть проверена на данных, вторая -- это перечень возможностей, а не рекомендация.
1. Потолок качения 0.008 занижен втрое
crr_from_accel: при accel_rms > 7 возвращается 0.008 и выше не поднимается.
0.008 -- это разбитый асфальт. Реальные грунты дают втрое больше.
Замер. Трек 6 сентября (id 1788683393199), километры 36.5-38.5: 1996 м,
ровно (уклон −0.1%), accel_rms медиана 15.1, скорость 13.8 км/ч.
Пользователь описал место как траву и тракторную колею с ямами.
Обратное решение уравнения мощности относительно Crr:
| если мощность была | требуемый Crr |
|---|---|
| 90 Вт | 0.0198 |
| 110 Вт | 0.0252 |
| 130 Вт | 0.0307 |
Справочные значения: плотный грунт 0.012, щебень 0.020, трава и песок 0.035. Замер попадает ровно в диапазон, соответствующий описанию.
Контрольная проверка обратным знаком. Те же вычисления на километрах 9-11 (25 км/ч, тряска 5.2) дают Crr отрицательным при 90-110 Вт -- значит там мощность была около 150 Вт. Сплиты на тех километрах показывают 148-172 Вт. Метод не завышает и не занижает систематически.
Предлагаемая шкала (взята нижняя граница замера -- лучше недосчитать):
< 3 -> 0.004 гладкий асфальт (как сейчас)
3-7 -> 0.006 смешанное (как сейчас)
7-11 -> 0.008 разбитый асфальт (как сейчас)
11-15 -> 0.012 плотный грунт (новое)
> 15 -> 0.020 трава, колея, щебень (новое)
Влияние на трек 6 сентября: средняя мощность 89 -> 108 Вт, механическая работа 1233 -> 1585 кДж. То есть на грунтах модель недосчитывала примерно четверть работы.
2. max(physics, MET) никогда не складывает, и подъёмы от этого страдают
На одних и тех же отрезках трека 6 сентября:
| участки | физика | MET | MET/физика |
|---|---|---|---|
| вся поездка | 1318 | 1764 | 1.34 |
| равнина, |уклон| < 1% | 727 | 1344 | 1.85 |
| в горку круче 2% | 400 | 165 | 0.41 |
Комментарий в коде говорит про «~2x занижение физики» -- на равнине это верно (1.85), но по поездке в целом всего 1.34, потому что на подъёмах физика выигрывает с большим запасом.
Изъян в том, что оценки не складываются. Биологические накладные расходы (размахивание ногами, равновесие, гашение тряски корпусом) никуда не деваются на подъёме, но там берётся физика и табличная оценка отбрасывается целиком. Направление ошибки известно: подъёмы недосчитываются.
Осмысленная альтернатива, если решите менять: не максимум, а
физика/КПД + накладные, где накладные оцениваются табличным минимумом
для медленной езды. Требует калибровки, поэтому просто как направление.
3. Ходьба с велосипедом засчитывается как езда
На односкоростном передача даёт точный признак: 40/18 на колесе 24" -- это 4.26 м за оборот шатунов. Скорость ниже 7.7 км/ч означает каденс ниже 30 об/мин, а на таком каденсе велосипед не едет, он падает. Значит человек идёт и ведёт или несёт его.
На треке 6 сентября таких кусков 0.66 км за 8:44 в движении -- пользователь подтвердил, что нёс велосипед руками в горку. Участок: км 56.31, длина 289 м, набор 47.8 м, уклон 16.6%, 4.3 км/ч.
Побочный эффект для статистики: если этот кусок не отделять, он склеивается с последующим ездовым подъёмом (км 56.60, 624 м, 3.8%, 12.3 км/ч) в один «подъём 915 м, 7.8%, 7.7 км/ч», которого в природе не было.
Признак применим только когда bikes.gear_count пусто или 1. На многоскоростном
низкая скорость ничего не доказывает.
По акселерометру это не различается, проверено: нести велосипед в гору 3.24,
ехать по гладкому асфальту 2.88, идти пешком (сессия walk) 3.98. accel_rms
показывает дорогу, а не походку.
4. Набор высоты расходится с независимым пересчётом в обе стороны
| трек | в базе | независимый пересчёт |
|---|---|---|
| 6 сентября | 328 м | 528 м |
| 4 сентября | 314 м | 264 м |
Знак разный, значит это не простое смещение. Без модели рельефа проверить нечем: попытка построить карту высот по своим же 156 поездкам провалилась -- перекрытие маршрутов всего 26% ячеек, а разброс высоты между поездками в одной и той же точке имеет медиану 10 метров.
Поправка после уточнения у пользователя. Изначально я счёл пустую
altitude_srtm_m (заполнена в одной сессии из 156) недоработкой. Это не так:
поле заполняется вручную кнопкой «уточнить высоты», которая ходит в GPXZ
и подобные сервисы, и пользователь жмёт её редко. Полагаться на сервисы
в поле нельзя -- РЭБ и РКН глушат, устойчиво работает преимущественно
проводной интернет.
Отсюда следует не «починить SRTM», а вопрос по существу: расхождение в наборе высоты остаётся неразрешённым, и разрешить его без внешнего рельефа нечем. Если это важно -- имеет смысл думать в сторону предварительной загрузки рельефа по району дома, по проводу, один раз. Если не важно -- достаточно знать, что цифра набора имеет точность порядка десятков процентов, и не строить на ней выводов. На расчёт мощности это влияет слабо: там работает уклон, а он считается по окну и ограничен сверху.
5. Пульса в точках нет, и это ожидаемо
gps_points.heart_rate равен нулю во всех 156 сессиях. Это не дефект
записи: пользователь потерял браслет и пульсометр не носит, писать в поле
попросту нечего. ble_hr_data.json и запись устройства в profile
остались от прежнего использования.
Упомянуто только затем, чтобы никто не искал ошибку там, где её нет, и чтобы разбор треков не обещал анализ по пульсу.
6. Ветер вычисляется из bearing_deg
Побочная находка, в приложении такого нет. Ветер виден по тому, что на ровных однотипных дорогах скорость зависит от курса. Берутся только точки с ровным уклоном, невысокой тряской и в седле, считается медианная скорость по восьми секторам курса, и подбирается ветер, при котором подразумеваемая мощность во всех секторах одинакова.
Трек 6 сентября: 6.3 км/ч из направления 345°. Разброс мощности по направлениям падает с 33% до 11%. Пользователь независимо подтвердил, что ветер часто дул в лицо.
Медианная скорость по курсу на этом треке -- 23.6 км/ч на юго-восток против 15.7 км/ч на северо-запад, при одинаковых дорогах.
Второй, независимый метод -- по встречным проездам одного места (758 пар) -- даёт 3.6 км/ч из 325°. Направление сходится в пределах 20°, сила -- в пределах множителя 1.8. Это и есть достижимая точность оценки ветра без датчика.
Разбор вариантов отслеживания ветра, включая идею пользователя про голосовые отметки, -- во второй части этого файла. Там же перечислены вопросы, по которым пользователь просит ваше мнение.
7. Ловушка при отделении ходьбы: порог по длительности не нужен
Найдено после замечания пользователя, что встречные проезды одного места случались и пешком.
Первая реализация признака ходьбы требовала, чтобы медленный кусок длился дольше 30 секунд подряд. Это оказалось неверно. На треке 6 сентября всего 14.7 минут движения медленнее 7.7 км/ч, из них сплошными кусками -- только 8.7 минуты. Оставшиеся 6 минут -- короткие эпизоды, и они попадали в статистику как педалирование.
Видно это по корзине каденса: «0-40 об/мин» занимала 21 минуту, и 6.4 минуты из неё приходилось на каденс ниже 30, которого при езде не бывает вовсе. После исправления корзина стала 9 минут, а средний каденс поднялся с 64 до 67.
Правильное разделение двух вопросов:
- Исключать из статистики нужно любую точку ниже порога, без оглядки на длительность: на этой передаче ниже 30 об/мин не едут, и точка. Сюда же попадут обычные торможения до нуля, но они короткие и маломощные, вреда нет.
- Называть кусок ходьбой в отчёте стоит только при длительности дольше 30 секунд подряд -- иначе отчёт будет объявлять ходьбой каждое торможение перед ямой.
На оценку ветра эта примесь, к счастью, не повлияла: там отдельный порог 9 км/ч, и пешие точки не проходили. Проверено прямым сравнением -- с ними 3.5 км/ч из 326°, без них 3.6 км/ч из 325°.
ЧАСТЬ ВТОРАЯ: предложения по отслеживанию ветра
Ниже -- не находки, а предположения, составленные по просьбе пользователя. Это перечень способов с замерами там, где замеры удалось сделать. Решение принимает сессия разработки.
Зачем
Ветер -- крупнейший неучтённый источник ошибки в расчёте мощности на равнине. На треке 6 сентября медианная скорость на одинаковых дорогах составила 23.6 км/ч на юго-восток против 15.7 км/ч на северо-запад. Разброс подразумеваемой мощности по направлениям -- 33%. Пока ветра в модели нет, эти 33% списываются на «просел» и «разогнался».
Ограничения, названные пользователем
- Сетевые сервисы в поле недоступны. РЭБ и РКН глушат, устойчиво работает преимущественно проводной интернет. Любая схема, которой нужна сеть во время поездки, нерабочая.
- Метеостанция меряет в другой точке. Стандартное измерение -- на высоте 10 м в открытом месте, усреднение за 10 минут. Велосипедист едет на высоте 1.5 м среди лесополос, домов и насыпей, где ветер и слабее, и порывистее. Среднее по области пользователю не поможет.
Оба возражения справедливы и отсекают наивное «дёргать погодный API».
Вариант 1. Считать ветер из самого трека (без железа и без сети)
Реализовано и проверено в tools/track/track.py. Два независимых метода.
1а. Подбор по секторам курса. Берутся только точки на ровном участке, с невысокой тряской, в седле. Медианная скорость считается по восьми секторам направления. Подбирается такой ветер, при котором подразумеваемая мощность во всех секторах одинакова.
1б. Встречные проезды одного места. Ищутся пары точек в одной ячейке 30 м, разнесённые по времени, с противоположными курсами, одинаковым покрытием и зеркальным уклоном. На одной и той же дороге качение и уклон сокращаются, и разница скоростей при равной мощности задаёт составляющую ветра вдоль дороги. Составляющие по разным дорогам сводятся в вектор методом наименьших квадратов.
Результат на треке 6 сентября:
| метод | скорость | направление |
|---|---|---|
| по секторам курса | 6.3 км/ч | 345° |
| по встречным проездам (758 пар) | 3.6 км/ч | 325° |
Направление сходится в пределах 20°, сила -- в пределах множителя 1.8.
Это и есть достижимая точность способа без железа: направление уверенно, сила -- с точностью до двух раз. Любой вариант с датчиком должен эту планку превосходить, иначе он не нужен.
Ограничения: работает только задним числом, требует разнообразия курсов (кольцевой маршрут или «туда-обратно» -- идеально, прямая в одну сторону -- бесполезно), и предполагает, что ветер за поездку не менялся. Последнее лечится подгонкой в скользящем окне 20-30 минут, но точность падает.
Вариант 2. Прогноз, скачанный ЗАРАНЕЕ по проводу
Обходит запрет на сеть в поле: перед выездом, на домашнем проводном интернете, скачивается почасовой прогноз ветра по району и кладётся в базу. В поездке сеть не нужна вообще.
Прогноз при этом используется не как истина, а как начальное приближение для варианта 1: он задаёт правдоподобный диапазон, а трек его уточняет. Возражение про «меряют в другой точке» остаётся в силе, но начальное приближение с известным смещением лучше, чем поиск вслепую.
Спорный момент: прогноз может оказаться хуже, чем ничего, если приземный ветер в лесополосах отличается от станционного в разы. Стоит проверить на нескольких треках, прежде чем закладывать.
Вариант 3. Трубка Пито на руле (единственный способ мерить по-настоящему)
Так устроены настоящие аэродинамические измерители для велосипеда (Notio, Aeropod). Трубка, направленная вперёд, плюс датчик разности давлений дают скорость набегающего воздуха напрямую, с частотой в десятки герц.
Разность между воздушной скоростью и путевой скоростью с GPS, вместе с курсом, даёт вектор ветра непрерывно, включая порывы. Сети не требуется вообще.
- датчик разности давлений класса MPXV7002DP плюс микроконтроллер с BLE
- порядок цены комплектующих -- полторы-три тысячи рублей
- BLE в приложении уже есть, канал знакомый
- нужна калибровка (сравнением с вариантом 1 на кольцевом маршруте в штиль) и защита от дождя и грязи
- трубка на руле уязвима при падении и при переноске велосипеда
Это единственный из перечисленных способов, который измеряет, а не оценивает. И единственный, который видит порывы, а не среднее.
Вариант 4. Флюгер на руле
Дешевле Пито: измеряется только угол набегающего потока относительно велосипеда, обычным поворотным датчиком. Скорости ветра не даёт, но сильно сужает решение в варианте 1 -- направление перестаёт быть неизвестным, остаётся подобрать одну величину вместо двух.
На велосипеде хрупок и чувствителен к завихрениям от самого седока.
Вариант 5. Голосовой ввод по словам-триггерам (идея пользователя)
Седок произносит «в лицо», «в спину», «боковой», «порыв», приложение ставит отметку времени. Распознавание на Android умеет работать без сети.
Замечание, которое считаю важным. Как измерение это слабо: оценка субъективна, отметки редкие, а говорить на тяжёлом дыхании и в шуме ветра неудобно -- причём шум ветра в микрофоне мешает именно тогда, когда ветер и надо отметить.
Но у этого есть куда более сильное применение: не как измерение, а как разметка для проверки автоматической оценки. Десяток отметок «в лицо» и «в спину» за поездку -- это готовый ответ, по которому можно проверить и откалибровать вариант 1, не покупая железа. Если оценка по треку совпадает с отметками -- ей можно верить и без отметок.
При таком применении редкость и субъективность отметок почти не мешают: проверяется знак и порядок величины, а не точное значение.
Вариант 6. Кнопка вместо голоса
То же самое, но нажатием: выносная кнопка на руле по BLE либо кнопка на гарнитуре. Надёжнее голоса на тяжёлом дыхании и в шуме, но выражает меньше -- реально различимы два-три состояния, не больше.
Вариант 7. Шум ветра в микрофоне
Обдув микрофона даёт характерный низкочастотный шум, растущий со скоростью потока. Теоретически из него извлекается воздушная скорость.
Практически: в кармане бесполезно, нужен вынесенный микрофон; шум дороги и шин мешает; калибровка своя для каждого телефона и чехла. Дёшево проверить, но рассчитывать не стоит. Упомянуто для полноты.
О чём хотелось бы услышать мнение сессии разработки
- Стоит ли вообще ветер в модели, или проще честно писать «мощность на равнине оценена с точностью ±30%» и не усложнять. Разброс 33% измерен, вопрос в том, окупается ли его устранение.
- Вариант 5 в роли разметки (а не измерения) -- насколько это ложится на текущий UI и на запись событий с отметкой времени. По цене внедрения это самый дешёвый из осмысленных шагов, и он не требует решать заранее, покупать ли датчик.
- Оправдан ли вариант 3. Планка задана: без железа выходит направление с точностью около 20° и сила с точностью до двух раз. Датчик за несколько тысяч рублей должен дать заметно больше, иначе он не нужен.
- Не ломает ли вариант 2 принцип автономности. Скачивание заранее по проводу формально сеть в поле не требует, но привязывает точность расчёта к тому, вспомнил ли пользователь нажать кнопку перед выездом. Это тот же изъян, что и у кнопки «уточнить высоты».
Готовый код обоих методов варианта 1 -- в tools/track/track.py
(функция wind_by_passes и секция «ветер» в report), выше описан
алгоритмически.
БЭКЛОГ: автоматический разбор повторов внутри одной поездки
Заказано пользователем 17 сентября 2026.
Задача
Пользователь катает интервалы: приезжает к подъёму, проезжает его 5 раз, скатываясь вниз между заездами, потом уезжает домой. Сейчас, чтобы разобрать такую тренировку, приходится либо останавливать и заново стартовать трек на каждом повторе (пользователь называет это бредом), либо разбирать вручную задним числом.
Нужно: один трек на всю тренировку, повторы находятся сами. Пользователь готов на отдельный режим «велотренировка», если так проще.
Что должно получиться на выходе
Таблица повторов: номер, время, средняя скорость, каденс, мощность. И главное число, по которому принимается решение о завтрашнем дне:
отношение последнего повтора к первому
Порог, выведенный из данных (11 и 17 сентября, по 5 повторов):
- последний быстрее или равен первому -- не на пределе, можно повторить завтра
- последний медленнее на 5-10% -- нормальная тяжёлая тренировка
- последний медленнее больше чем на 10% -- два дня спокойно
Алгоритм, проверен вручную на этих данных
Повторы ищутся по самоподобию трека, без опорных точек от пользователя.
- Разложить точки поездки по ячейкам ~10 м (сетка как в
heightmap.py). - Найти ячейки, посещённые больше одного раза, и отбросить те, где
направление движения (
bearing_deg) при посещениях различается больше чем на 60 градусов: проезд вниз по той же дороге повтором не считается. Без проверки направления половина повторов теряется или считается задом наперёд -- проверено, это главная ловушка. - Собрать самую длинную цепочку таких ячеек, идущую подряд по ходу движения. Это и есть повторяемый участок. Требовать длину не меньше 300 м и не меньше 3 проходов, иначе это перекрёсток у дома, а не подъём.
- Разметить проходы: для каждого прохода записать вход и выход по цепочке, время, длину.
- Нормировать время на общую длину: у пользователя 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 м. Уклоны на этом участке выдуманы целиком.
Признаки негодной высоты, годятся как автотест
Все три считаются по уже записанным данным, без сети:
elevation_gain_mв сессии и набор, пересчитанный по точкам, расходятся больше чем вдвое в любую сторону- медиана модуля разности высоты между соседними точками выше 1.5 м
- доля точек с шагом записи больше 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 не входит.
Отсюда два следствия:
- Датасет будет не тот, которым уточнены нынешние 12 сессий. Смешивать нельзя, и колонка источника из пункта 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 выше) не гипотетическая опасность, а уже случившийся факт: база заполнена последним источником в цепочке, и об этом никто не знал.
- Колонка источника (пункт 2) обязательна.
- Отказ надо показывать пользователю, а не писать в консоль.
Покупать ли 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.