fix: GPS from SQLite framekms (confirmed live device schema)
odc-api.db confirmed present on device. framekms table has: latitude, longitude, altitude, hdop, satellites_used, time NOT lat_deg/lon_deg/alt_m/num_satellites as previously assumed. Redis fallback retained, supports both field naming conventions. API response format unchanged (still returns lat_deg/lon_deg for client compat).
This commit is contained in:
parent
75a8321014
commit
bbb0d0d676
1 changed files with 74 additions and 27 deletions
|
|
@ -1,40 +1,87 @@
|
||||||
"""Redis client for GPS and IMU data."""
|
"""GPS data reader for adacam-api.
|
||||||
|
Primary source: /data/recording/odc-api.db framekms table (confirmed schema from live device).
|
||||||
|
Fallback: Redis (keys may appear when device has outdoor GPS fix with custom firmware).
|
||||||
|
|
||||||
|
Confirmed field names from live device:
|
||||||
|
latitude, longitude, altitude, hdop, satellites_used, speed, heading, time
|
||||||
|
"""
|
||||||
import json
|
import json
|
||||||
import redis
|
import os
|
||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
|
|
||||||
_client = None
|
ODC_DB = '/data/recording/odc-api.db'
|
||||||
|
GNSS_REDIS_KEYS = ['GNSSFusion30Hz', 'GnssData', 'GnssPvt', 'GnssPosition']
|
||||||
|
|
||||||
|
_redis_client = None
|
||||||
|
|
||||||
|
|
||||||
def get_client():
|
def _get_redis():
|
||||||
"""Get Redis connection."""
|
global _redis_client
|
||||||
global _client
|
try:
|
||||||
if _client is None:
|
import redis
|
||||||
_client = redis.Redis(host="localhost", port=6379, decode_responses=True)
|
if _redis_client is None:
|
||||||
return _client
|
_redis_client = redis.Redis(host='localhost', port=6379, decode_responses=True)
|
||||||
|
_redis_client.ping()
|
||||||
|
return _redis_client
|
||||||
|
except Exception:
|
||||||
|
_redis_client = None
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_latest_gnss():
|
def get_latest_gnss():
|
||||||
"""Get latest GPS fix from GNSSFusion30Hz ZSET."""
|
"""Get latest GPS fix. Returns dict with keys: lat_deg, lon_deg, alt_m, hdop, num_satellites.
|
||||||
client = get_client()
|
Field names kept compatible with original API response format."""
|
||||||
try:
|
|
||||||
# Get the most recent entry (highest score = most recent timestamp)
|
# Primary: SQLite framekms (confirmed live device schema)
|
||||||
results = client.zrevrange("GNSSFusion30Hz", 0, 0, withscores=True)
|
if os.path.exists(ODC_DB):
|
||||||
if results:
|
try:
|
||||||
data = json.loads(results[0][0])
|
conn = sqlite3.connect(ODC_DB)
|
||||||
return {
|
row = conn.execute(
|
||||||
"lat_deg": data.get("lat_deg"),
|
'SELECT latitude, longitude, altitude, hdop, satellites_used, time '
|
||||||
"lon_deg": data.get("lon_deg"),
|
'FROM framekms WHERE latitude IS NOT NULL AND latitude != 0 '
|
||||||
"alt_m": data.get("alt_m"),
|
'ORDER BY time DESC LIMIT 1'
|
||||||
"unix_milliseconds": int(data.get("unix_milliseconds", 0)),
|
).fetchone()
|
||||||
"hdop": data.get("hdop"),
|
conn.close()
|
||||||
"num_satellites": data.get("num_satellites", 0),
|
if row:
|
||||||
}
|
return {
|
||||||
except (redis.RedisError, json.JSONDecodeError):
|
'lat_deg': row[0],
|
||||||
pass
|
'lon_deg': row[1],
|
||||||
|
'alt_m': row[2],
|
||||||
|
'hdop': row[3],
|
||||||
|
'num_satellites': row[4],
|
||||||
|
'unix_milliseconds': int(row[5]) if row[5] else int(time.time() * 1000),
|
||||||
|
}
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fallback: Redis (may appear with custom firmware with outdoor GPS fix)
|
||||||
|
r = _get_redis()
|
||||||
|
if r:
|
||||||
|
for key in GNSS_REDIS_KEYS:
|
||||||
|
try:
|
||||||
|
for getter in [lambda k: r.zrevrange(k, 0, 0), lambda k: [r.get(k)]]:
|
||||||
|
items = getter(key)
|
||||||
|
item = items[0] if items else None
|
||||||
|
if item:
|
||||||
|
data = json.loads(item)
|
||||||
|
lat = data.get('latitude') or data.get('lat_deg')
|
||||||
|
lon = data.get('longitude') or data.get('lon_deg')
|
||||||
|
if lat and lon:
|
||||||
|
return {
|
||||||
|
'lat_deg': lat,
|
||||||
|
'lon_deg': lon,
|
||||||
|
'alt_m': data.get('altitude') or data.get('alt_m', 0),
|
||||||
|
'hdop': data.get('hdop', 99),
|
||||||
|
'num_satellites': data.get('satellites_used') or data.get('num_satellites', 0),
|
||||||
|
'unix_milliseconds': int(data.get('unix_milliseconds', time.time() * 1000)),
|
||||||
|
}
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def has_gps_lock():
|
def has_gps_lock():
|
||||||
"""Check if we have a valid GPS lock."""
|
"""Check if we have a valid GPS lock."""
|
||||||
gnss = get_latest_gnss()
|
gnss = get_latest_gnss()
|
||||||
return gnss is not None and gnss.get("num_satellites", 0) >= 4
|
return gnss is not None and gnss.get('num_satellites', 0) >= 4
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue