generated from Grigo/AndroidTemplate
fix elevation
This commit is contained in:
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user