fix elevation

This commit is contained in:
2026-06-16 12:04:23 +03:00
parent 6b34e75f35
commit dbef86d2c9
4 changed files with 74 additions and 33 deletions
+1 -1
View File
@@ -379,7 +379,7 @@ def health():
return {
"ok": status["db_ok"],
"ts": time.time(),
"api_build": "2026-06-16e",
"api_build": "2026-06-16f",
**status,
**elevation_status(),
}
+69 -31
View File
@@ -59,8 +59,8 @@
.radio-compare-head { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 6px; font-weight: 600; }
.radio-row { display: grid; grid-template-columns: 72px 1fr 1fr; gap: 6px; padding: 2px 0; }
.radio-label { color: #aaa; }
.radio-tx { color: #e94560; }
.radio-rx { color: #4fc3f7; }
.radio-tx { color: #e94560; min-width: 0; word-break: break-all; }
.radio-rx { color: #4fc3f7; min-width: 0; word-break: break-all; }
.radio-row .changed, .changed { background: #e9456033; border-radius: 3px; padding: 0 2px; }
.radio-static summary { cursor: pointer; color: #aaa; margin: 4px 0; }
#stats .changed { background: #e9456033; border-radius: 3px; }
@@ -361,7 +361,7 @@
{ position: 'topright', collapsed: true }
).addTo(map);
const API_BUILD = '2026-06-16e';
const API_BUILD = '2026-06-16f';
const markers = {};
let selectedId = null;
@@ -511,7 +511,10 @@
const p = Math.max(0, Math.min(1, progress));
if (points.length === 1) {
const one = points[0];
return { lat: Number(one.lat), lon: Number(one.lon), meta: one.meta, rssi: one.rssi };
return {
lat: Number(one.lat), lon: Number(one.lon), meta: one.meta, rssi: one.rssi,
pointTs: Number(one.ts),
};
}
const f = p * (points.length - 1);
const i = Math.floor(f);
@@ -520,13 +523,17 @@
const a = points[i];
const b = points[j];
if (frac <= 0 || i === j) {
return { lat: Number(a.lat), lon: Number(a.lon), meta: a.meta, rssi: a.rssi };
return {
lat: Number(a.lat), lon: Number(a.lon), meta: a.meta, rssi: a.rssi,
pointTs: Number(a.ts),
};
}
return {
lat: Number(a.lat) + (Number(b.lat) - Number(a.lat)) * frac,
lon: Number(a.lon) + (Number(b.lon) - Number(a.lon)) * frac,
meta: frac < 0.5 ? a.meta : b.meta,
rssi: frac < 0.5 ? a.rssi : b.rssi,
pointTs: Number(frac < 0.5 ? a.ts : b.ts),
};
}
@@ -564,7 +571,7 @@
function snapAtCursor(track, telemetryRows, cursor, roleFallback) {
const pos = trackPointAt(track, cursor);
if (pos?.meta && String(pos.meta).length > 2) {
const ts = timelineUseProgress ? null : cursor.t;
const ts = pos.pointTs ?? (timelineUseProgress ? null : cursor.t);
return snapFromTrackPoint(pos, ts, roleFallback || track?.role);
}
if (timelineUseProgress) {
@@ -613,11 +620,12 @@
function snapFromTrackPoint(pos, t, roleFallback) {
if (!pos) return null;
const ts = pos.pointTs ?? t;
return {
meta: pos.meta,
role: roleFallback,
rssi: pos.rssi,
ts: t,
ts,
lat: pos.lat,
lon: pos.lon,
};
@@ -625,7 +633,12 @@
function mergeTelCoords(tel, pos) {
if (!tel || !pos) return tel;
return { ...tel, lat: pos.lat, lon: pos.lon };
return {
...tel,
lat: pos.lat,
lon: pos.lon,
ts: tel.ts ?? pos.pointTs ?? null,
};
}
function snapAtTime(track, telemetryRows, t, roleFallback) {
@@ -772,30 +785,45 @@
return `${Number(tel.lat).toFixed(5)}, ${Number(tel.lon).toFixed(5)}`;
}
function formatPacketTime(tel) {
if (!tel) return null;
let ts = tel.ts;
if (tel.meta) {
let o = tel.meta;
if (typeof o === 'string') {
try { o = JSON.parse(o); } catch (e) { o = null; }
}
if (o) {
if (o.packet_ts != null) ts = o.packet_ts;
else if (o.ts != null) ts = o.ts;
else if (o.fields) {
for (const [k, v] of Object.entries(o.fields)) {
if (/time/i.test(k)) return String(v);
}
}
}
}
if (ts == null || !Number.isFinite(Number(ts)) || Number(ts) <= 1) return null;
function formatTsValue(ts) {
if (ts == null || !Number.isFinite(Number(ts))) return null;
const n = Number(ts);
if (n <= 1e8) return null;
const ms = n < 1e12 ? n * 1000 : n;
return new Date(ms).toLocaleTimeString();
}
function packetTimeFromMetaFields(fields) {
if (!fields) return null;
const skip = /timeout|on\s*air|speed|airtime/i;
for (const [k, v] of Object.entries(fields)) {
const key = String(k).trim();
if (skip.test(key)) continue;
if (!/^(time|timestamp|packet.?time)$/i.test(key)) continue;
const text = String(v).trim();
if (/^\d+(\.\d+)?\s*ms$/i.test(text)) continue;
const parsed = formatTsValue(text);
if (parsed) return parsed;
if (text) return text;
}
return null;
}
function formatPacketTime(tel) {
if (!tel) return null;
const direct = formatTsValue(tel.ts);
if (direct) return direct;
if (!tel.meta) return null;
let o = tel.meta;
if (typeof o === 'string') {
try { o = JSON.parse(o); } catch (e) { return null; }
}
if (!o) return null;
const fromMeta = formatTsValue(o.stats_at) || formatTsValue(o.packet_ts) || formatTsValue(o.ts);
if (fromMeta) return fromMeta;
return packetTimeFromMetaFields(o.fields);
}
function enrichSnapFromTel(snap, tel) {
snap.gps = formatCoords(tel) || '—';
snap.packetTime = formatPacketTime(tel) || '—';
@@ -1942,10 +1970,16 @@
const t0 = Number(first.ts);
const t1 = Number(last.ts);
if (tNum <= t0) {
return { lat: Number(first.lat), lon: Number(first.lon), meta: first.meta, rssi: first.rssi };
return {
lat: Number(first.lat), lon: Number(first.lon), meta: first.meta, rssi: first.rssi,
pointTs: t0,
};
}
if (tNum >= t1) {
return { lat: Number(last.lat), lon: Number(last.lon), meta: last.meta, rssi: last.rssi };
return {
lat: Number(last.lat), lon: Number(last.lon), meta: last.meta, rssi: last.rssi,
pointTs: t1,
};
}
for (let i = 0; i < points.length - 1; i++) {
const a = points[i];
@@ -1959,11 +1993,15 @@
lat: Number(a.lat) + (Number(b.lat) - Number(a.lat)) * f,
lon: Number(a.lon) + (Number(b.lon) - Number(a.lon)) * f,
meta: tNum - ta < tb - tNum ? a.meta : b.meta,
rssi: tNum - ta < tb - tNum ? a.rssi : b.rssi
rssi: tNum - ta < tb - tNum ? a.rssi : b.rssi,
pointTs: tNum - ta < tb - tNum ? ta : tb,
};
}
}
return { lat: Number(last.lat), lon: Number(last.lon), meta: last.meta, rssi: last.rssi };
return {
lat: Number(last.lat), lon: Number(last.lon), meta: last.meta, rssi: last.rssi,
pointTs: t1,
};
}
function overlapRange(txPts, rxPts) {
+1
View File
@@ -61,6 +61,7 @@
if (o.rx_pkt_per_s != null) snap.rxPktPerS = Number(o.rx_pkt_per_s);
if (o.per_percent != null) snap.perPercent = Number(o.per_percent);
if (o.rx_quality_percent != null) snap.rxQualityPercent = Number(o.rx_quality_percent);
if (o.stats_at != null) snap.statsAt = Number(o.stats_at);
if (o.fields && typeof o.fields === 'object') {
for (const [k, v] of Object.entries(o.fields)) {
if (!isKnownLabel(k)) snap.extraFields[k] = String(v);