auto: update RTK rover/base + config (2026-05-01 17:00:43)
This commit is contained in:
+25
-1
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import sys, time, struct, argparse, threading, os, sqlite3, uuid, select, termios, tty
|
import sys, time, struct, argparse, threading, os, sqlite3, uuid, select, termios, tty, json
|
||||||
from typing import Optional, Tuple, Dict
|
from typing import Optional, Tuple, Dict
|
||||||
import serial
|
import serial
|
||||||
from serial.serialutil import SerialException
|
from serial.serialutil import SerialException
|
||||||
@@ -570,6 +570,8 @@ def main():
|
|||||||
help="Start SQLite recording automatically on service start")
|
help="Start SQLite recording automatically on service start")
|
||||||
ap.add_argument("--record-note", default="auto",
|
ap.add_argument("--record-note", default="auto",
|
||||||
help="Note stored in sessions table when --auto-record is used")
|
help="Note stored in sessions table when --auto-record is used")
|
||||||
|
ap.add_argument("--cmd-file", default="/home/naue/rtk/rtk_rover_cmd.json",
|
||||||
|
help="Command file for web UI start/stop recording")
|
||||||
|
|
||||||
ap.add_argument("--site-log", action="store_true",
|
ap.add_argument("--site-log", action="store_true",
|
||||||
help="Enable construction-site FIX-only CSV/GeoJSON/status logging")
|
help="Enable construction-site FIX-only CSV/GeoJSON/status logging")
|
||||||
@@ -667,6 +669,28 @@ def main():
|
|||||||
recorder.start(note="manual")
|
recorder.start(note="manual")
|
||||||
print(f"⏺️ Recording START (session={recorder.session_id})")
|
print(f"⏺️ Recording START (session={recorder.session_id})")
|
||||||
|
|
||||||
|
# Web/API command file: {"cmd":"start"} or {"cmd":"stop"}
|
||||||
|
try:
|
||||||
|
if args.cmd_file and os.path.exists(args.cmd_file):
|
||||||
|
with open(args.cmd_file, "r", encoding="utf-8") as f:
|
||||||
|
cmd = json.load(f)
|
||||||
|
try:
|
||||||
|
os.remove(args.cmd_file)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
action = str(cmd.get("cmd", "")).lower()
|
||||||
|
note = str(cmd.get("note", "web"))
|
||||||
|
|
||||||
|
if action == "start" and not recorder.is_recording():
|
||||||
|
recorder.start(note=note)
|
||||||
|
print(f"⏺️ Web Recording START (session={recorder.session_id})", flush=True)
|
||||||
|
elif action == "stop" and recorder.is_recording():
|
||||||
|
recorder.stop()
|
||||||
|
print("⏹️ Web Recording STOP", flush=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"WEB CMD ERROR: {e}", flush=True)
|
||||||
|
|
||||||
f = read_ubx_frame(zed, timeout=0.05)
|
f = read_ubx_frame(zed, timeout=0.05)
|
||||||
pvt = None
|
pvt = None
|
||||||
hpp = None
|
hpp = None
|
||||||
|
|||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sqlite3, json, time, os
|
||||||
|
from flask import Flask, render_template, request, Response, redirect, url_for
|
||||||
|
|
||||||
|
DB_PATH = "/home/naue/rtk/rtk_rover.db"
|
||||||
|
CMD_FILE = "/home/naue/rtk/rtk_rover_cmd.json"
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
def db():
|
||||||
|
con = sqlite3.connect(DB_PATH)
|
||||||
|
con.row_factory = sqlite3.Row
|
||||||
|
return con
|
||||||
|
|
||||||
|
def write_cmd(cmd):
|
||||||
|
tmp = CMD_FILE + ".tmp"
|
||||||
|
with open(tmp, "w", encoding="utf-8") as f:
|
||||||
|
json.dump({"cmd": cmd, "note": "web", "ts": time.time()}, f)
|
||||||
|
os.replace(tmp, CMD_FILE)
|
||||||
|
|
||||||
|
@app.route("/", methods=["GET"])
|
||||||
|
def index():
|
||||||
|
con = db()
|
||||||
|
sessions = con.execute("""
|
||||||
|
SELECT session_id,
|
||||||
|
datetime(start_ts,'unixepoch','localtime') AS start_time,
|
||||||
|
CASE WHEN end_ts IS NULL THEN '' ELSE datetime(end_ts,'unixepoch','localtime') END AS end_time,
|
||||||
|
note,
|
||||||
|
end_ts
|
||||||
|
FROM sessions
|
||||||
|
ORDER BY start_ts DESC
|
||||||
|
LIMIT 50
|
||||||
|
""").fetchall()
|
||||||
|
|
||||||
|
active_session = con.execute("""
|
||||||
|
SELECT session_id, datetime(start_ts,'unixepoch','localtime') AS start_time, note
|
||||||
|
FROM sessions
|
||||||
|
WHERE end_ts IS NULL
|
||||||
|
ORDER BY start_ts DESC
|
||||||
|
LIMIT 1
|
||||||
|
""").fetchone()
|
||||||
|
|
||||||
|
session_id = request.args.get("session_id") or (sessions[0]["session_id"] if sessions else "")
|
||||||
|
only_fix = request.args.get("only_fix", "0") == "1"
|
||||||
|
|
||||||
|
params = []
|
||||||
|
where = ""
|
||||||
|
if session_id:
|
||||||
|
where += " WHERE session_id=?"
|
||||||
|
params.append(session_id)
|
||||||
|
if only_fix:
|
||||||
|
where += " AND carr_soln=2" if where else " WHERE carr_soln=2"
|
||||||
|
|
||||||
|
points = con.execute(f"""
|
||||||
|
SELECT datetime(unix_ts,'unixepoch','localtime') AS t,
|
||||||
|
lat, lon, hmsl_corr_m, hacc_m, vacc_m, carr_soln, num_sats, source
|
||||||
|
FROM gnss_pvt
|
||||||
|
{where}
|
||||||
|
ORDER BY unix_ts DESC
|
||||||
|
LIMIT 200
|
||||||
|
""", params).fetchall()
|
||||||
|
|
||||||
|
count = con.execute(f"SELECT COUNT(*) AS n FROM gnss_pvt {where}", params).fetchone()["n"]
|
||||||
|
|
||||||
|
return render_template("index.html",
|
||||||
|
sessions=sessions,
|
||||||
|
session_id=session_id,
|
||||||
|
only_fix=only_fix,
|
||||||
|
points=points,
|
||||||
|
count=count,
|
||||||
|
active_session=active_session)
|
||||||
|
|
||||||
|
@app.route("/record/start", methods=["POST"])
|
||||||
|
def record_start():
|
||||||
|
write_cmd("start")
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
@app.route("/record/stop", methods=["POST"])
|
||||||
|
def record_stop():
|
||||||
|
write_cmd("stop")
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
@app.route("/export.csv")
|
||||||
|
def export_csv():
|
||||||
|
session_id = request.args.get("session_id")
|
||||||
|
only_fix = request.args.get("only_fix", "0") == "1"
|
||||||
|
|
||||||
|
params = []
|
||||||
|
where = ""
|
||||||
|
if session_id:
|
||||||
|
where += " WHERE session_id=?"
|
||||||
|
params.append(session_id)
|
||||||
|
if only_fix:
|
||||||
|
where += " AND carr_soln=2" if where else " WHERE carr_soln=2"
|
||||||
|
|
||||||
|
con = db()
|
||||||
|
rows = con.execute(f"""
|
||||||
|
SELECT datetime(unix_ts,'unixepoch','localtime') AS t,
|
||||||
|
lat, lon, hmsl_m, hmsl_corr_m, hacc_m, vacc_m,
|
||||||
|
fix_type, carr_soln, diff_soln, gnss_ok, num_sats, source
|
||||||
|
FROM gnss_pvt
|
||||||
|
{where}
|
||||||
|
ORDER BY unix_ts ASC
|
||||||
|
""", params).fetchall()
|
||||||
|
|
||||||
|
def generate():
|
||||||
|
yield "time,lat,lon,hmsl_m,hmsl_corr_m,hacc_m,vacc_m,fix_type,carr_soln,diff_soln,gnss_ok,num_sats,source\n"
|
||||||
|
for r in rows:
|
||||||
|
yield ",".join(str(r[k]) if r[k] is not None else "" for k in r.keys()) + "\n"
|
||||||
|
|
||||||
|
return Response(generate(), mimetype="text/csv",
|
||||||
|
headers={"Content-Disposition": "attachment; filename=rtk_export.csv"})
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(host="0.0.0.0", port=8080)
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>RTK Rover DB</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; margin: 2rem; background: #f6f6f6; }
|
||||||
|
h1 { margin-top: 0; }
|
||||||
|
.card { background: white; padding: 1rem; border-radius: 10px; margin-bottom: 1rem; box-shadow: 0 1px 5px #ccc; }
|
||||||
|
table { width: 100%; border-collapse: collapse; background: white; }
|
||||||
|
th, td { padding: .45rem; border-bottom: 1px solid #ddd; font-size: .9rem; }
|
||||||
|
th { text-align: left; background: #eee; }
|
||||||
|
.fix { color: green; font-weight: bold; }
|
||||||
|
.float { color: orange; font-weight: bold; }
|
||||||
|
select, button, input { padding: .4rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>RTK Rover Datenbank</h1>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<form method="get">
|
||||||
|
<label>Session:</label>
|
||||||
|
<select name="session_id">
|
||||||
|
{% for s in sessions %}
|
||||||
|
<option value="{{s.session_id}}" {% if s.session_id == session_id %}selected{% endif %}>
|
||||||
|
{{s.start_time}} | {{s.note}} | {{s.session_id[:8]}}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="only_fix" value="1" {% if only_fix %}checked{% endif %}>
|
||||||
|
nur RTK FIX
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit">Anzeigen</button>
|
||||||
|
<a href="/export.csv?session_id={{session_id}}{% if only_fix %}&only_fix=1{% endif %}">CSV exportieren</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<b>Punkte:</b> {{count}} |
|
||||||
|
<b>Anzeige:</b> letzte {{points|length}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Zeit</th>
|
||||||
|
<th>Lat</th>
|
||||||
|
<th>Lon</th>
|
||||||
|
<th>hMSL corr</th>
|
||||||
|
<th>hAcc</th>
|
||||||
|
<th>vAcc</th>
|
||||||
|
<th>RTK</th>
|
||||||
|
<th>Sats</th>
|
||||||
|
<th>Quelle</th>
|
||||||
|
</tr>
|
||||||
|
{% for p in points %}
|
||||||
|
<tr>
|
||||||
|
<td>{{p.t}}</td>
|
||||||
|
<td>{{"%.8f"|format(p.lat)}}</td>
|
||||||
|
<td>{{"%.8f"|format(p.lon)}}</td>
|
||||||
|
<td>{{"%.3f"|format(p.hmsl_corr_m)}}</td>
|
||||||
|
<td>{{"%.3f"|format(p.hacc_m)}}</td>
|
||||||
|
<td>{{"%.3f"|format(p.vacc_m)}}</td>
|
||||||
|
<td class="{% if p.carr_soln == 2 %}fix{% elif p.carr_soln == 1 %}float{% endif %}">
|
||||||
|
{% if p.carr_soln == 2 %}FIX{% elif p.carr_soln == 1 %}FLOAT{% else %}—{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{{p.num_sats}}</td>
|
||||||
|
<td>{{p.source}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user