Compare commits

..

2 Commits

Author SHA1 Message Date
Smittix 7ed039564b v2.26.11: fix APRS map ignoring configured observer position (#193)
The APRS map initialisation only checked for a live GPS fix, falling
back to the centre of the US (39.8N, 98.6W) when none was available.
It never read the observer position configured in .env via
INTERCEPT_DEFAULT_LAT / INTERCEPT_DEFAULT_LON.

Seed aprsUserLocation from ObserverLocation.getShared() (or the
Jinja-injected defaults) on page load so the map centres on the
user's configured position and distance calculations work without GPS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 18:17:50 +00:00
Smittix 8adfb3a40a v2.26.10: fix APRS stop timeout and inverted SDR device status (#194)
The APRS stop endpoint terminated two processes sequentially (up to 4s
with PROCESS_TERMINATE_TIMEOUT=2s each) while the frontend fetch timed
out at 2.2s. This caused console errors and left the SDR device claimed
in the registry until termination finished, making the status panel show
the device as active after the user clicked stop.

Fix: release the SDR device from the registry immediately inside the
lock, clear process references, then terminate processes in a background
thread so the HTTP response returns instantly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 18:14:21 +00:00
5 changed files with 69 additions and 21 deletions
+14
View File
@@ -2,6 +2,20 @@
All notable changes to iNTERCEPT will be documented in this file.
## [2.26.11] - 2026-03-14
### Fixed
- **APRS map ignores configured observer position** — The APRS map always fell back to the centre of the US (39.8°N, 98.6°W) when no live GPS fix was available, ignoring the observer position configured in `.env` (`INTERCEPT_DEFAULT_LAT` / `INTERCEPT_DEFAULT_LON`). Now seeds the APRS user location from the shared observer location on page load, so the map centres correctly and distance calculations work. (#193)
---
## [2.26.10] - 2026-03-14
### Fixed
- **APRS stop timeout and inverted SDR device status** — The APRS stop endpoint terminated two processes sequentially (up to 4s) while the frontend timed out at 2.2s, causing console errors and the SDR status panel to show stale state (active after stop, idle during use). Now releases the SDR device immediately and terminates processes in a background thread so the response returns instantly. (#194)
---
## [2.26.9] - 2026-03-14
### Fixed
+3 -3
View File
@@ -7,15 +7,15 @@ import os
import sys
# Application version
VERSION = "2.26.9"
VERSION = "2.26.11"
# Changelog - latest release notes (shown on welcome screen)
CHANGELOG = [
{
"version": "2.26.9",
"version": "2.26.11",
"date": "March 2026",
"highlights": [
"ADS-B bias-t fallback via rtl_biast for Blog V4 when dump1090 lacks native support",
"APRS map now centres on configured observer position from .env",
]
},
{
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "intercept"
version = "2.26.9"
version = "2.26.11"
description = "Signal Intelligence Platform - Pager/433MHz/ADS-B/Satellite/WiFi/Bluetooth"
readme = "README.md"
requires-python = ">=3.9"
+30 -16
View File
@@ -1924,7 +1924,13 @@ def start_aprs() -> Response:
@aprs_bp.route('/stop', methods=['POST'])
def stop_aprs() -> Response:
"""Stop APRS decoder."""
"""Stop APRS decoder.
Releases the SDR device immediately so the status panel updates
without waiting for process termination. Process cleanup runs in a
background thread to avoid blocking the HTTP response (which caused
frontend timeout errors when two processes each took up to 2s to die).
"""
global aprs_active_device, aprs_active_sdr_type
with app_module.aprs_lock:
@@ -1939,6 +1945,28 @@ def stop_aprs() -> Response:
if not processes_to_stop:
return api_error('APRS decoder not running', 400)
# Release SDR device immediately so status panel reflects the
# change without waiting for process termination.
if aprs_active_device is not None:
app_module.release_sdr_device(aprs_active_device, aprs_active_sdr_type or 'rtlsdr')
aprs_active_device = None
aprs_active_sdr_type = None
# Capture refs to clear before releasing the lock
master_fd = getattr(app_module, 'aprs_master_fd', None)
app_module.aprs_process = None
if hasattr(app_module, 'aprs_rtl_process'):
app_module.aprs_rtl_process = None
app_module.aprs_master_fd = None
# Terminate processes in background so the response returns fast.
# Each proc.wait() can block up to PROCESS_TERMINATE_TIMEOUT (2s),
# which previously caused the frontend 2200ms fetch to abort.
def _cleanup():
# Close PTY master fd first — this unblocks the stream thread
if master_fd is not None:
with contextlib.suppress(OSError):
os.close(master_fd)
for proc in processes_to_stop:
try:
proc.terminate()
@@ -1948,21 +1976,7 @@ def stop_aprs() -> Response:
except Exception as e:
logger.error(f"Error stopping APRS process: {e}")
# Close PTY master fd
if hasattr(app_module, 'aprs_master_fd') and app_module.aprs_master_fd is not None:
with contextlib.suppress(OSError):
os.close(app_module.aprs_master_fd)
app_module.aprs_master_fd = None
app_module.aprs_process = None
if hasattr(app_module, 'aprs_rtl_process'):
app_module.aprs_rtl_process = None
# Release SDR device
if aprs_active_device is not None:
app_module.release_sdr_device(aprs_active_device, aprs_active_sdr_type or 'rtlsdr')
aprs_active_device = None
aprs_active_sdr_type = None
threading.Thread(target=_cleanup, daemon=True).start()
return jsonify({'status': 'stopped'})
+21 -1
View File
@@ -9849,8 +9849,28 @@
let aprsMeterCheckInterval = null;
const APRS_METER_TIMEOUT = 5000; // 5 seconds for "no signal" state
// APRS user location (from GPS)
// APRS user location (from GPS or shared observer location)
let aprsUserLocation = { lat: null, lon: null };
// Seed from configured observer location so the map centres on the
// user's position even without a live GPS fix.
(function _seedAprsLocation() {
if (typeof ObserverLocation !== 'undefined' && ObserverLocation.getShared) {
const shared = ObserverLocation.getShared();
if (shared && shared.lat && shared.lon) {
aprsUserLocation.lat = shared.lat;
aprsUserLocation.lon = shared.lon;
return;
}
}
// Fallback: read the Jinja-injected defaults directly
const lat = window.INTERCEPT_DEFAULT_LAT;
const lon = window.INTERCEPT_DEFAULT_LON;
if (lat && lon && Number.isFinite(lat) && Number.isFinite(lon)) {
aprsUserLocation.lat = lat;
aprsUserLocation.lon = lon;
}
})();
let aprsUserMarker = null;
// Calculate distance in miles using Haversine formula