From f4ef87705ce73ec0f3b8cb84eab53bdb78f9ab93 Mon Sep 17 00:00:00 2001 From: grigo Date: Wed, 17 Jun 2026 11:22:15 +0300 Subject: [PATCH] fixed gistogramm --- server/fastapi_app.py | 2 +- server/static/index.html | 40 ++++++---------- server/static/quality-viz.js | 88 +----------------------------------- 3 files changed, 16 insertions(+), 114 deletions(-) diff --git a/server/fastapi_app.py b/server/fastapi_app.py index 66060de..eeb7a24 100644 --- a/server/fastapi_app.py +++ b/server/fastapi_app.py @@ -379,7 +379,7 @@ def health(): return { "ok": status["db_ok"], "ts": time.time(), - "api_build": "2026-06-16h", + "api_build": "2026-06-16i", **status, **elevation_status(), } diff --git a/server/static/index.html b/server/static/index.html index b16ac79..0ea3061 100644 --- a/server/static/index.html +++ b/server/static/index.html @@ -48,18 +48,13 @@ #elevationCanvas.elev-probe { cursor: crosshair; } .elev-legend { font-size: 0.7rem; } #qualityVizPanel { - display: none; margin-top: 8px; gap: 8px; - grid-template-columns: 140px 1fr; align-items: start; - } - #qualityVizPanel.visible { display: grid; } - @media (max-width: 700px) { - #qualityVizPanel.visible { grid-template-columns: 1fr; } + display: none; margin-top: 8px; } + #qualityVizPanel.visible { display: block; } .quality-viz-box { background: #0f3460; border: 1px solid #444; border-radius: 6px; padding: 6px; } .quality-viz-title { font-size: 0.7rem; color: #aaa; margin-bottom: 4px; } - #qualityRoseCanvas { width: 100%; height: 140px; display: block; background: #0a0a14; border-radius: 4px; } #qualityDistCanvas { width: 100%; height: 140px; display: block; background: #0a0a14; border-radius: 4px; } #timelineStatsPanel { display: none; margin-top: 10px; padding-top: 10px; border-top: 1px solid #333; @@ -121,8 +116,8 @@ #pairedStatus { font-size: 0.75rem; color: #aaa; margin-top: 4px; } .muted { color: #aaa; font-size: 0.75rem; } .legend { font-size: 0.75rem; color: #ccc; } - .legend-tx { color: #e94560; } - .legend-rx { color: #4fc3f7; } + .legend-tx { color: #4fc3f7; } + .legend-rx { color: #e94560; } #mapModal { display: none; position: fixed; z-index: 2000; min-width: 260px; max-width: 360px; max-height: 70vh; overflow: auto; @@ -337,12 +332,8 @@
-
Качество по направлению TX→RX
- -
-
-
RX Quality vs расстояние
- +
RX Quality vs расстояние TX↔RX
+
@@ -387,7 +378,7 @@ { position: 'topright', collapsed: true } ).addTo(map); - const API_BUILD = '2026-06-16h'; + const API_BUILD = '2026-06-16i'; const markers = {}; let selectedId = null; @@ -470,9 +461,8 @@ const TRACKS_POLL_MS = 10000; const TELEMETRY_POLL_MS = 2000; - const TX_COLOR = '#e94560'; - const RX_COLOR = '#4fc3f7'; - const GHOST_TX_COLOR = '#ff9800'; + const TX_COLOR = '#4fc3f7'; + const RX_COLOR = '#e94560'; map.on('zoomend moveend', () => { if (!programmaticMove) userMovedMap = true; @@ -1201,9 +1191,7 @@ const quality = rxQualityFromMeta(rxPos.meta); if (quality == null) continue; const distM = haversineM(txPos.lat, txPos.lon, rxPos.lat, rxPos.lon); - const bearing = QualityViz.bearingDeg( - txPos.lat, txPos.lon, rxPos.lat, rxPos.lon); - samples.push({ distM, bearing, quality }); + samples.push({ distM, quality }); } return samples; } @@ -1231,9 +1219,7 @@ return; } qualitySamplesCache = buildQualitySamples(); - const roseCanvas = document.getElementById('qualityRoseCanvas'); const distCanvas = document.getElementById('qualityDistCanvas'); - QualityViz.drawQualityRose(roseCanvas, qualitySamplesCache, qualityColor); QualityViz.drawQualityDistChart( distCanvas, qualitySamplesCache, qualityColor, currentTimelineLinkDist()); panel?.classList.add('visible'); @@ -2404,12 +2390,14 @@ if (txPos) { ghostTx = L.circleMarker([txPos.lat, txPos.lon], { - radius: 10, color: '#fff', fillColor: GHOST_TX_COLOR, fillOpacity: 0.9, weight: 3 + radius: 10, color: '#fff', fillColor: TX_COLOR, fillOpacity: 0.95, weight: 3 }).addTo(map); } if (rxPos) { + const q = rxQualityFromMeta(rxPos.meta); + const rxGhostColor = q != null ? (qualityColor(q) || RX_COLOR) : RX_COLOR; ghostRx = L.circleMarker([rxPos.lat, rxPos.lon], { - radius: 10, color: RX_COLOR, fillColor: RX_COLOR, fillOpacity: 0.9, weight: 3 + radius: 10, color: '#fff', fillColor: rxGhostColor, fillOpacity: 0.95, weight: 3 }).addTo(map); } if (txPos && rxPos) { diff --git a/server/static/quality-viz.js b/server/static/quality-viz.js index e75c112..53e4099 100644 --- a/server/static/quality-viz.js +++ b/server/static/quality-viz.js @@ -1,91 +1,7 @@ -/** Polar rose and quality-vs-distance charts for TX/RX track compare. */ +/** RX quality vs distance chart for TX/RX track compare. */ (function (global) { 'use strict'; - function bearingDeg(lat1, lon1, lat2, lon2) { - const toRad = d => d * Math.PI / 180; - const toDeg = r => r * 180 / Math.PI; - const dLon = toRad(lon2 - lon1); - const y = Math.sin(dLon) * Math.cos(toRad(lat2)); - const x = Math.cos(toRad(lat1)) * Math.sin(toRad(lat2)) - - Math.sin(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.cos(dLon); - return (toDeg(Math.atan2(y, x)) + 360) % 360; - } - - function drawQualityRose(canvas, samples, qualityColor) { - if (!canvas) return; - const ctx = canvas.getContext('2d'); - const w = canvas.clientWidth || 140; - const h = canvas.clientHeight || 140; - if (canvas.width !== w) canvas.width = w; - if (canvas.height !== h) canvas.height = h; - ctx.fillStyle = '#0a0a14'; - ctx.fillRect(0, 0, w, h); - - const sectors = 16; - const cx = w / 2; - const cy = h / 2; - const maxR = Math.min(w, h) * 0.38; - - if (!samples?.length) { - ctx.fillStyle = '#888'; - ctx.font = '11px system-ui'; - ctx.textAlign = 'center'; - ctx.fillText('нет данных качества', cx, cy); - return; - } - - const bins = Array.from({ length: sectors }, () => ({ sum: 0, count: 0 })); - const step = 360 / sectors; - for (const s of samples) { - const idx = Math.floor(((s.bearing % 360) + 360) % 360 / step) % sectors; - bins[idx].sum += s.quality; - bins[idx].count += 1; - } - const maxCount = Math.max(1, ...bins.map(b => b.count)); - - ctx.strokeStyle = '#333'; - ctx.beginPath(); - ctx.arc(cx, cy, maxR, 0, Math.PI * 2); - ctx.stroke(); - - for (let i = 0; i < sectors; i++) { - const start = (i * step - 90) * Math.PI / 180; - const end = ((i + 1) * step - 90) * Math.PI / 180; - const b = bins[i]; - if (!b.count) continue; - const avgQ = b.sum / b.count; - const r = maxR * (0.15 + 0.85 * (b.count / maxCount)); - const col = qualityColor ? qualityColor(avgQ) : '#888'; - ctx.beginPath(); - ctx.moveTo(cx, cy); - ctx.arc(cx, cy, r, start, end); - ctx.closePath(); - ctx.fillStyle = col; - ctx.globalAlpha = 0.85; - ctx.fill(); - ctx.globalAlpha = 1; - ctx.strokeStyle = '#222'; - ctx.lineWidth = 0.5; - ctx.stroke(); - } - - ctx.fillStyle = '#aaa'; - ctx.font = '9px system-ui'; - ctx.textAlign = 'center'; - ctx.fillText('N', cx, cy - maxR - 4); - ctx.fillText('S', cx, cy + maxR + 10); - ctx.textAlign = 'left'; - ctx.fillText('E', cx + maxR + 4, cy + 3); - ctx.textAlign = 'right'; - ctx.fillText('W', cx - maxR - 4, cy + 3); - - ctx.fillStyle = '#888'; - ctx.font = '8px system-ui'; - ctx.textAlign = 'center'; - ctx.fillText('длина ∝ число точек', cx, h - 4); - } - function drawQualityDistChart(canvas, samples, qualityColor, highlightDist) { if (!canvas) return; const ctx = canvas.getContext('2d'); @@ -175,8 +91,6 @@ } global.QualityViz = { - bearingDeg, - drawQualityRose, drawQualityDistChart, }; })(typeof window !== 'undefined' ? window : globalThis);