# telemetry-dashboard Одностраничный дэшборд телеметрии стратосферного зонда. Подключается к WebSocket бэкенда stratoflights и отображает все поля MSG_BEACON в реальном времени. Запускается рядом с leaflet_svelte (карта — там, здесь — приборы). ## Быстрый старт ```bash npm install # дев-сервер npm run dev # type-check + продакшн-сборка (статика в ./build) npm run check npm run build ``` Деплой идентичен leaflet_svelte: `build/` раздаётся любым статическим сервером, fallback — `index.html`. ## Использование В шапке указывается полный адрес WS-эндпоинта: ``` ws:///api/ws/satellite//telemetry/ ``` Адрес сохраняется в localStorage (дефолт можно задать через `VITE_DEFAULT_WS_URL`, см. `.env.example`). Если URL совпадает с известным шаблоном stratoflights, перед подключением подтягивается история по REST (`/api//telemetry/`); для любого другого ws-адреса дэшборд просто слушает поток. При обрыве связи — автопереподключение с экспоненциальным бэкоффом (до 30 с), данные не сбрасываются, плитки затемняются, возраст последнего пакета подсвечивается (янтарный > 15 с, красный > 60 с). ## Тестовый сервер ```bash pip install websockets python3 test_server.py --interval 1 --speedup 20 # полный полёт за ~9 минут python3 test_server.py --speedup 200 --start 5900 # сразу перед разрывом шара ``` Эмулирует полный профиль полёта (подъём 5 м/с → разрыв на 30 км → спуск) в точном формате пакетов бэкенда. Подключаться на `ws://localhost:8765/`. ## Демо бок-о-бок с leaflet_svelte (через Django, как в проде) ```bash scripts/demo-stack.sh # Django :8000 + leaflet :5173 + дэшборд :5174 + станция scripts/demo-stack.sh restart-flight # полёт заново: рестарт станции + чистка истории в БД scripts/demo-stack.sh stop ``` Станция летит по `mock_prediction.json` из корня репозиториев (реальный предикт) со случайным дрейфом до ±5 км. Каждый запуск/`restart-flight` начинает полёт с нуля; после него нажми «Отключиться/Подключиться» в обоих окнах, чтобы сбросить старые графики. Темп: `SPEEDUP=60 scripts/demo-stack.sh`. Скрипт также поднимает **фейковый predictor на :8080** (захардкоженный адрес `TawhiriClient` в Django), который отдаёт тот же `mock_prediction.json` — поэтому «Рассчитать» в leaflet возвращает ровно ту траекторию, по которой летит станция. Порядок для оверлея прогноза в слежении: `/predict` → «Рассчитать» → перейти на «Слежение» **кликом в навбаре** (результат прогноза живёт в памяти SPA и не переживает перезагрузку страницы) → выбрать рабочую область в «Сравнении с прогнозом». Если на :8080 уже работает настоящий predictor — скрипт его не трогает и прогнозы идут через него. График отклонений сопоставляет точки по времени: при ускоренном полёте (speedup > 1) времена телеметрии и прогноза расходятся — для честного сравнения ставь дату запуска в форме «сейчас» и `SPEEDUP=1`. Скрипт (адаптация leaflet_svelte/scripts/run-stack.sh под новые пути) сам создаёт спутник и токен, запускает `test_server.py --push` как наземную станцию и печатает UUID спутника. Дальше: - **leaflet**: http://localhost:5173/track → логин demo/demo → вставить UUID → «Подключиться»; - **дэшборд**: http://localhost:5174/ → адрес `ws://localhost:8000/api/ws/satellite//telemetry/` → «Подключиться» (адрес запоминается в localStorage; история за прошедший полёт подтянется по REST автоматически). Оба окна читают один и тот же поток из Django. Если фронты уже запущены руками — скрипт их не трогает, просто вставь UUID в оба окна. ## Пре-флайт аудит форматов ```bash python3 field_audit.py # из корня telemetry-dashboard ``` Исполняемая сверка всей цепочки: математика firmware против контрольных примеров из даташитов (MS5611, SHT4x), бит-точный round-trip beacon → PN9/CRC → Deframer → parse → webclient, совпадение ключей payload и имён флагов с нашим парсером. 9 групп проверок; любое расхождение — assert. ## Устройство - **SvelteKit + adapter-static** (Svelte 5 runes, TypeScript) — тот же стек и процесс сборки, что у leaflet_svelte; тот же брендовый Bootstrap (`static/css/bootstrap.min.css`). - **uPlot** для графиков — им же рендерит time series Grafana; ~22 КБ gzip, потоковые обновления через `setData()`, курсор синхронизирован между всеми графиками (`cursor.sync`). - `src/lib/telemetry.ts` — чистый домен: парсинг пакета, производная вертикальная скорость (окно 30 с, как у SondeHub), колонки для uPlot с разрывами линий при пропаже связи > 60 с. - `src/lib/store.svelte.ts` — WebSocket + реконнект + массив сэмплов. - Пороги (батарея, спутники, возраст пакета, быстрый спуск) — константы в начале `src/routes/+page.svelte`. ## Выбор визуализаций (по итогам ресёрча SondeHub/SatNOGS/Grafana-практик) | Показатель | Форма | |---|---| | Возраст пакета | stat-плитка, верхний левый угол, порог 15/60 с | | Высота GPS + баро | hero-график (две серии; баро выявляет отказ MS5611) + плитка | | Верт. скорость | производная по GPS-высоте; плитка ▲/▼ + график с нулевой линией | | Давление | график, лог-шкала (экспонента → прямая), гПа | | Температуры ×3 | один график, три серии с прямой подписью | | Влажность | график с фиксированной шкалой 0–100 % | | Батарея | плитка + спарклайн, порог 3,5/3,3 В (3,3 = BATT_LOW_MV firmware) | | Спутники | плитка + спарклайн, порог 4/6 | | Координаты | моноширинная плитка, 5 знаков | | 13 флагов | лампы с семантикой: OK-флаги зелёный/красный, состояния синий/серый, LOW_BATT/PYRO_ARMED — тревожные только когда взведены | Радиальных gauge нет намеренно: ни один осмотренный боевой дэшборд (sonik.space, SondeHub, SatNOGS) их не использует.