diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 77c4caf..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Sulkta Coop - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 6770372..801a225 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Config file: `/data/adacam/config.json` ```json { "device_id": "auto-generated UUID", - "adamaps_key": "", + "adamaps_key": "***REMOVED***", "adamaps_api": "https://api.adamaps.org", "ap_interface": "wlp1s0f0", "tunnel_host": "", diff --git a/adacam_api/app.py b/adacam_api/app.py index be0384b..03f0afb 100644 --- a/adacam_api/app.py +++ b/adacam_api/app.py @@ -112,7 +112,6 @@ def create_app(): # ── DEBUG ENDPOINTS ──────────────────────────────────────────────────── @app.route('/api/1/debug/redis-keys') - @require_auth def redis_keys(): """Debug endpoint — list Redis keys for GPS/IMU troubleshooting.""" try: diff --git a/adacam_api/config.py b/adacam_api/config.py index 2898690..746d609 100644 --- a/adacam_api/config.py +++ b/adacam_api/config.py @@ -8,7 +8,7 @@ FIRMWARE_VERSION = "adacam-1.0.0" _defaults = { "device_id": None, - "adamaps_key": "", + "adamaps_key": "***REMOVED***", "adamaps_api": "https://api.adamaps.org", "ap_interface": "wlp1s0f0", "tunnel_host": "", diff --git a/adacam_api/redis_client.py b/adacam_api/redis_client.py index 80162b9..78a2f53 100644 --- a/adacam_api/redis_client.py +++ b/adacam_api/redis_client.py @@ -1,87 +1,40 @@ -"""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 post-liberation). - -Confirmed field names from live device: - latitude, longitude, altitude, hdop, satellites_used, speed, heading, time -""" +"""Redis client for GPS and IMU data.""" import json -import os -import sqlite3 -import time +import redis -ODC_DB = '/data/recording/odc-api.db' -GNSS_REDIS_KEYS = ['GNSSFusion30Hz', 'GnssData', 'GnssPvt', 'GnssPosition'] - -_redis_client = None +_client = None -def _get_redis(): - global _redis_client - try: - import redis - if _redis_client is None: - _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_client(): + """Get Redis connection.""" + global _client + if _client is None: + _client = redis.Redis(host="localhost", port=6379, decode_responses=True) + return _client def get_latest_gnss(): - """Get latest GPS fix. Returns dict with keys: lat_deg, lon_deg, alt_m, hdop, num_satellites. - Field names kept compatible with original API response format.""" - - # Primary: SQLite framekms (confirmed live device schema) - if os.path.exists(ODC_DB): - try: - conn = sqlite3.connect(ODC_DB) - row = conn.execute( - 'SELECT latitude, longitude, altitude, hdop, satellites_used, time ' - 'FROM framekms WHERE latitude IS NOT NULL AND latitude != 0 ' - 'ORDER BY time DESC LIMIT 1' - ).fetchone() - conn.close() - if row: - return { - 'lat_deg': row[0], - '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 post-liberation 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 + """Get latest GPS fix from GNSSFusion30Hz ZSET.""" + client = get_client() + try: + # Get the most recent entry (highest score = most recent timestamp) + results = client.zrevrange("GNSSFusion30Hz", 0, 0, withscores=True) + if results: + data = json.loads(results[0][0]) + return { + "lat_deg": data.get("lat_deg"), + "lon_deg": data.get("lon_deg"), + "alt_m": data.get("alt_m"), + "unix_milliseconds": int(data.get("unix_milliseconds", 0)), + "hdop": data.get("hdop"), + "num_satellites": data.get("num_satellites", 0), + } + except (redis.RedisError, json.JSONDecodeError): + pass return None def has_gps_lock(): """Check if we have a valid GPS lock.""" 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 diff --git a/requirements.txt b/requirements.txt index 4ec13a8..6f89858 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -flask>=2.3.2 -redis>=5.0 -requests>=2.32.0 +flask>=2.0 +redis>=4.0 +requests>=2.25