generated from Grigo/AndroidTemplate
added GFSK
This commit is contained in:
Binary file not shown.
+14
-1
@@ -435,11 +435,24 @@ def list_tracks(device_id: Optional[str] = None, limit: int = 50) -> list[dict[s
|
||||
WHERE p.track_id = t.id AND p.role IS NOT NULL AND p.role != ''
|
||||
ORDER BY p.ts DESC LIMIT 1)
|
||||
"""
|
||||
modem_mode_sub = """
|
||||
(SELECT COALESCE(
|
||||
json_extract(p.meta, '$.modem_mode'),
|
||||
CASE
|
||||
WHEN json_extract(p.meta, '$.bitrate_bps') IS NOT NULL THEN 'GFSK'
|
||||
WHEN json_extract(p.meta, '$.spreading_factor') IS NOT NULL THEN 'LORA'
|
||||
ELSE NULL
|
||||
END
|
||||
) FROM track_points p
|
||||
WHERE p.track_id = t.id AND p.meta IS NOT NULL AND p.meta != ''
|
||||
ORDER BY p.ts DESC LIMIT 1)
|
||||
"""
|
||||
track_cols = f"""
|
||||
SELECT t.id, t.device_id, t.started_at, t.ended_at, t.label,
|
||||
d.label AS device_label,
|
||||
(SELECT COUNT(*) FROM track_points p WHERE p.track_id = t.id) AS point_count,
|
||||
{role_sub} AS role
|
||||
{role_sub} AS role,
|
||||
{modem_mode_sub} AS modem_mode
|
||||
FROM tracks t
|
||||
LEFT JOIN devices d ON d.device_id = t.device_id
|
||||
"""
|
||||
|
||||
+43
-6
@@ -1028,6 +1028,37 @@ function referenceSessionById(id) {
|
||||
return referencePointSessions.find(s => String(s.id) === String(id)) || null;
|
||||
}
|
||||
|
||||
function modemModeFromTxParams(params) {
|
||||
if (!params) return null;
|
||||
if (params.modem_mode) return RadioUI.normalizeModemMode(params.modem_mode);
|
||||
if (params.bitrate_bps != null) return RadioUI.MODEM_GFSK;
|
||||
if (params.spreading_factor != null) return RadioUI.MODEM_LORA;
|
||||
return null;
|
||||
}
|
||||
|
||||
function modemModeFromTrack(track) {
|
||||
if (!track) return null;
|
||||
if (track.modem_mode) return RadioUI.normalizeModemMode(track.modem_mode);
|
||||
const points = track.points;
|
||||
if (!points?.length) return null;
|
||||
for (let i = points.length - 1; i >= 0; i--) {
|
||||
let meta = points[i].meta;
|
||||
if (!meta) continue;
|
||||
if (typeof meta === 'string') {
|
||||
try { meta = JSON.parse(meta); } catch (e) { continue; }
|
||||
}
|
||||
if (meta.modem_mode) return RadioUI.normalizeModemMode(meta.modem_mode);
|
||||
if (meta.bitrate_bps != null) return RadioUI.MODEM_GFSK;
|
||||
if (meta.spreading_factor != null) return RadioUI.MODEM_LORA;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function modemModeSuffix(mode) {
|
||||
const label = mode ? RadioUI.modemModeLabel(mode) : '';
|
||||
return label && label !== '—' ? ` · ${label}` : '';
|
||||
}
|
||||
|
||||
function renderReferencePointList() {
|
||||
const list = document.getElementById('referencePointList');
|
||||
if (!list) return;
|
||||
@@ -1043,9 +1074,10 @@ function renderReferencePointList() {
|
||||
if (checked) row.classList.add('selected');
|
||||
const ctx = session.reference_context || {};
|
||||
const dist = ctx.distance_m != null ? ` · ${Number(ctx.distance_m).toFixed(0)} м` : '';
|
||||
const mode = modemModeSuffix(modemModeFromTxParams(ctx.tx_params));
|
||||
row.innerHTML = `<input type="checkbox" ${checked ? 'checked' : ''} />
|
||||
<span><span class="preset-title">${escapeHtml(session.label || 'Репер #' + session.id)}</span>
|
||||
<span class="preset-desc">#${session.id} · RX${dist}</span></span>`;
|
||||
<span class="preset-desc">#${session.id} · RX${mode}${dist}</span></span>`;
|
||||
const checkbox = row.querySelector('input');
|
||||
checkbox.addEventListener('change', () => {
|
||||
const sid = String(session.id);
|
||||
@@ -1093,13 +1125,14 @@ function showReferencePointsOnMap() {
|
||||
if (!rxPos || rxPos.lat == null || rxPos.lon == null) return;
|
||||
const rxLatLng = [Number(rxPos.lat), Number(rxPos.lon)];
|
||||
bounds.push(rxLatLng);
|
||||
const refMode = modemModeSuffix(modemModeFromTxParams(ctx.tx_params));
|
||||
const rxMarker = L.circleMarker(rxLatLng, {
|
||||
radius: 9,
|
||||
color: '#00ff88',
|
||||
weight: 2,
|
||||
fillColor: '#00ff88',
|
||||
fillOpacity: 0.25
|
||||
}).addTo(map).bindPopup(`RX репер #${session.id}`);
|
||||
}).addTo(map).bindPopup(`RX репер #${session.id}${refMode}`);
|
||||
let txMarker = null;
|
||||
let line = null;
|
||||
if (txPos && txPos.lat != null && txPos.lon != null) {
|
||||
@@ -1259,8 +1292,9 @@ async function showReferencePoint(sessionId) {
|
||||
const data = await res.json();
|
||||
const stats = data.stats || {};
|
||||
const ctx = data.reference_context || {};
|
||||
const refMode = modemModeSuffix(modemModeFromTxParams(ctx.tx_params));
|
||||
if (summary) {
|
||||
summary.innerHTML = `<div><b>${escapeHtml(data.label || 'Реперная точка')} #${data.id}</b> · RX</div>
|
||||
summary.innerHTML = `<div><b>${escapeHtml(data.label || 'Реперная точка')} #${data.id}</b> · RX${refMode}</div>
|
||||
<div>${escapeHtml(deviceDisplayName(data))} · samples: ${data.sample_count || 0} · ${escapeHtml(data.status || '')}</div>
|
||||
<div class="metric-card"><b>Контекст</b>
|
||||
<div>${escapeHtml(formatReferenceTxParams(ctx.tx_params))}</div>
|
||||
@@ -4329,8 +4363,9 @@ async function refreshTimelineTelemetry() {
|
||||
function trackOptionLabel(t) {
|
||||
const start = new Date(t.started_at * 1000).toLocaleString();
|
||||
const role = t.role ? ` · ${t.role}` : '';
|
||||
const mode = modemModeSuffix(modemModeFromTrack(t));
|
||||
const dev = t.device_id ? ` · ${deviceDisplayName(t)}` : '';
|
||||
return `#${t.id}${role}${dev} · ${start} (${t.point_count})`;
|
||||
return `#${t.id}${mode}${role}${dev} · ${start} (${t.point_count})`;
|
||||
}
|
||||
|
||||
async function loadAllTracks() {
|
||||
@@ -4408,7 +4443,7 @@ async function showSingleTrack() {
|
||||
updateTimelineAtSingle(timelineCursor());
|
||||
}
|
||||
document.getElementById('trackInfo').textContent =
|
||||
`Трек #${loadedSingleTrack.id} (${loadedSingleTrack.points.length} точек)`;
|
||||
`Трек #${loadedSingleTrack.id} (${loadedSingleTrack.points.length} точек)${modemModeSuffix(modemModeFromTrack(loadedSingleTrack))}`;
|
||||
updateTrackButtons();
|
||||
}
|
||||
|
||||
@@ -4470,8 +4505,10 @@ async function showDualTracks() {
|
||||
|
||||
rebuildQualityViz();
|
||||
const modeHint = range && range.mode === 'union' ? ' · без пересечения по времени' : '';
|
||||
const txMode = modemModeSuffix(modemModeFromTrack(loadedTxTrack));
|
||||
const rxMode = modemModeSuffix(modemModeFromTrack(loadedRxTrack));
|
||||
document.getElementById('trackInfo').textContent =
|
||||
`TX #${loadedTxTrack.id} (${loadedTxTrack.points.length}) + RX #${loadedRxTrack.id} (${loadedRxTrack.points.length})${modeHint}`;
|
||||
`TX #${loadedTxTrack.id} (${loadedTxTrack.points.length})${txMode} + RX #${loadedRxTrack.id} (${loadedRxTrack.points.length})${rxMode}${modeHint}`;
|
||||
updateTrackButtons();
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -91,6 +91,33 @@ def test_sync_track_create_offline(temp_db, monkeypatch):
|
||||
|
||||
|
||||
def test_tracks_crud(temp_db):
|
||||
storage.init_db()
|
||||
start = storage.start_track("android-12345678")
|
||||
tid = start["track_id"]
|
||||
storage.add_track_points(
|
||||
tid,
|
||||
[
|
||||
{
|
||||
"ts": 1.0,
|
||||
"lat": 55.75,
|
||||
"lon": 37.62,
|
||||
"rssi": -70.0,
|
||||
"role": "TX",
|
||||
"meta": '{"modem_mode":"GFSK","bitrate_bps":20000}',
|
||||
}
|
||||
],
|
||||
)
|
||||
fin = storage.finish_track(tid)
|
||||
assert fin["point_count"] == 1
|
||||
listed = storage.list_tracks(limit=10)
|
||||
gfsk = next((t for t in listed if t["id"] == tid), None)
|
||||
assert gfsk is not None
|
||||
assert gfsk["modem_mode"] == "GFSK"
|
||||
track = storage.get_track(tid)
|
||||
assert len(track["points"]) == 1
|
||||
|
||||
|
||||
def test_tracks_crud_legacy(temp_db):
|
||||
storage.init_db()
|
||||
start = storage.start_track("android-12345678")
|
||||
tid = start["track_id"]
|
||||
|
||||
Reference in New Issue
Block a user