Compare commits

..

8 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
Smittix 9a9b1e9856 v2.26.9: add rtl_biast fallback for ADS-B bias-t on Blog V4 (#195)
When dump1090 lacks native --enable-biast support, the system now falls
back to rtl_biast (RTL-SDR Blog drivers) to enable bias-t power before
starting dump1090. The Blog V4's built-in LNA requires bias-t to
receive ADS-B signals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 18:05:15 +00:00
Smittix 8aeb52380e v2.26.8: fix acarsdec build failure on macOS (#187)
HOST_NAME_MAX is Linux-specific and undefined on macOS, causing 3
compile errors in acarsdec.c. Now patched with #define HOST_NAME_MAX 255
before building. Also fixed deprecated -Ofast flag on all macOS archs
(was only patched for arm64).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 16:03:46 +00:00
Smittix 05141b9a1b v2.26.7: fix health check SDR detection on macOS (#188)
timeout (GNU coreutils) is not available on macOS, causing rtl_test to
silently fail and report no SDR device found. Now tries timeout, then
gtimeout (Homebrew coreutils), then falls back to background process
with manual kill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 16:01:55 +00:00
Smittix dc0850d339 v2.26.6: fix oversized branded 'i' logo on dashboard pages (#189)
.logo span { display: inline } in dashboard CSS had specificity (0,1,1),
overriding .brand-i { display: inline-block } at (0,1,0). Inline elements
ignore width/height, so the SVG rendered at intrinsic size (~80px tall).
Added .logo .brand-i selector at (0,2,0) to retain inline-block display.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 15:57:25 +00:00
Smittix 2bbf896e7c v2.26.5: fix database errors crashing entire UI (#190)
get_setting() now catches sqlite3.OperationalError and returns the
default value. Previously, an inaccessible database (e.g. root-owned
instance/ from sudo) caused inject_offline_settings to crash every
page render with 500 Internal Server Error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 15:49:29 +00:00
Smittix faf57741a1 v2.26.4: fix Environment Configurator crash when .env variable missing (#191)
read_env_var() grep pipeline failed under set -euo pipefail when .env
existed but didn't contain the requested key. grep returned 1 (no match),
pipefail propagated it, and set -e killed the script.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 15:44:48 +00:00
9 changed files with 252 additions and 53 deletions
+56
View File
@@ -2,6 +2,62 @@
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
- **ADS-B bias-t support for RTL-SDR Blog V4** — When dump1090 lacks native `--enable-biast` support, the system now falls back to `rtl_biast` (from RTL-SDR Blog drivers) to enable bias-t power before starting dump1090. The Blog V4's built-in LNA requires bias-t to receive ADS-B signals. (#195)
---
## [2.26.8] - 2026-03-14
### Fixed
- **acarsdec build failure on macOS** — `HOST_NAME_MAX` is Linux-specific (`<limits.h>`) and undefined on macOS, causing 3 compile errors in `acarsdec.c`. Now patched with `#define HOST_NAME_MAX 255` before building. Also fixed deprecated `-Ofast` flag warning on all macOS architectures (was only patched for arm64). (#187)
---
## [2.26.7] - 2026-03-14
### Fixed
- **Health check SDR detection on macOS** — `timeout` (GNU coreutils) is not available on macOS, causing `rtl_test` to silently fail and report "No RTL-SDR device found" even when one is connected. Now tries `timeout`, then `gtimeout` (Homebrew coreutils), then falls back to a background process with manual kill. (#188)
---
## [2.26.6] - 2026-03-14
### Fixed
- **Oversized branded 'i' logo on dashboards** — `.logo span { display: inline }` in dashboard CSS had higher specificity (0,1,1) than `.brand-i { display: inline-block }` (0,1,0), forcing the branded "i" SVG to render as inline which ignores width/height. Added `.logo .brand-i` selector (0,2,0) to retain `inline-block` display. (#189)
---
## [2.26.5] - 2026-03-14
### Fixed
- **Database errors crash entire UI** — `get_setting()` now catches `sqlite3.OperationalError` and returns the default value instead of propagating the exception. Previously, if the database was inaccessible (e.g. root-owned `instance/` directory from running with `sudo`), the `inject_offline_settings` context processor would crash every page render with a 500 Internal Server Error. (#190)
---
## [2.26.4] - 2026-03-14
### Fixed
- **Environment Configurator crash** — `read_env_var()` crashed with "Setup failed at line 2333" when `.env` existed but didn't contain the variable being looked up. `grep` returned exit code 1 (no match), which `pipefail` propagated and `set -e` turned into a fatal error. Fixed by appending `|| true` to the pipeline. (#191)
---
## [2.26.3] - 2026-03-13
### Fixed
+43 -1
View File
@@ -7,10 +7,52 @@ import os
import sys
# Application version
VERSION = "2.26.3"
VERSION = "2.26.11"
# Changelog - latest release notes (shown on welcome screen)
CHANGELOG = [
{
"version": "2.26.11",
"date": "March 2026",
"highlights": [
"APRS map now centres on configured observer position from .env",
]
},
{
"version": "2.26.8",
"date": "March 2026",
"highlights": [
"Fix acarsdec build failure on macOS (HOST_NAME_MAX undefined)",
]
},
{
"version": "2.26.7",
"date": "March 2026",
"highlights": [
"Fix health check SDR detection on macOS (timeout command not available)",
]
},
{
"version": "2.26.6",
"date": "March 2026",
"highlights": [
"Fix oversized branded 'i' logo on Aircraft & Vessel dashboards",
]
},
{
"version": "2.26.5",
"date": "March 2026",
"highlights": [
"Fix database errors crashing the entire UI — pages now degrade gracefully",
]
},
{
"version": "2.26.4",
"date": "March 2026",
"highlights": [
"Fix Environment Configurator crash when .env exists but variable is missing",
]
},
{
"version": "2.26.3",
"date": "March 2026",
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "intercept"
version = "2.26.3"
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'})
+32 -4
View File
@@ -174,7 +174,7 @@ read_env_var() {
local fallback="${2:-}"
if [[ -f "$SCRIPT_DIR/.env" ]]; then
local val
val=$(grep -E "^${key}=" "$SCRIPT_DIR/.env" 2>/dev/null | tail -1 | cut -d'=' -f2-)
val=$(grep -E "^${key}=" "$SCRIPT_DIR/.env" 2>/dev/null | tail -1 | cut -d'=' -f2- || true)
if [[ -n "$val" ]]; then
# Strip surrounding quotes
val="${val#\"}"
@@ -751,9 +751,26 @@ install_acarsdec_from_source_macos() {
cd "$tmp_dir/acarsdec"
# Replace deprecated -Ofast (all macOS, not just arm64)
if grep -q '\-Ofast' CMakeLists.txt 2>/dev/null; then
sed -i '' 's/-Ofast/-O3 -ffast-math/g' CMakeLists.txt
info "Patched deprecated -Ofast flag"
fi
# macOS doesn't have -march=native on arm64
if [[ "$(uname -m)" == "arm64" ]]; then
sed -i '' 's/-Ofast -march=native/-O3 -ffast-math/g' CMakeLists.txt
info "Patched compiler flags for Apple Silicon (arm64)"
sed -i '' 's/ -march=native//g' CMakeLists.txt
info "Removed -march=native for Apple Silicon"
fi
# HOST_NAME_MAX is Linux-specific; macOS uses _POSIX_HOST_NAME_MAX
if grep -q 'HOST_NAME_MAX' acarsdec.c 2>/dev/null; then
sed -i '' '1i\
#ifndef HOST_NAME_MAX\
#define HOST_NAME_MAX 255\
#endif
' acarsdec.c
info "Patched HOST_NAME_MAX for macOS compatibility"
fi
if grep -q 'pthread_tryjoin_np' rtl.c 2>/dev/null; then
@@ -1924,7 +1941,18 @@ do_health_check() {
info "SDR device detection..."
if cmd_exists rtl_test; then
local rtl_output
rtl_output=$(timeout 3 rtl_test -d 0 2>&1 || true)
if cmd_exists timeout; then
rtl_output=$(timeout 3 rtl_test -d 0 2>&1 || true)
elif cmd_exists gtimeout; then
rtl_output=$(gtimeout 3 rtl_test -d 0 2>&1 || true)
else
# No timeout command (common on macOS) — run with background kill
rtl_test -d 0 > /tmp/.rtl_test_out 2>&1 & local rtl_pid=$!
sleep 2
kill "$rtl_pid" 2>/dev/null; wait "$rtl_pid" 2>/dev/null
rtl_output=$(cat /tmp/.rtl_test_out 2>/dev/null || true)
rm -f /tmp/.rtl_test_out
fi
if echo "$rtl_output" | grep -q "Found\|Using device"; then
ok "RTL-SDR device detected"
((pass++)) || true
+5 -2
View File
@@ -88,8 +88,11 @@
}
/* Branded "i" — inline SVG that matches the logo icon.
Sized to 0.9em so it sits naturally alongside text at any font-size. */
.brand-i {
Sized to 0.9em so it sits naturally alongside text at any font-size.
Uses .logo .brand-i (0,2,0) to beat .logo span (0,1,1) in dashboard CSS
which otherwise forces display:inline and breaks width/height. */
.brand-i,
.logo .brand-i {
display: inline-block;
width: 0.55em;
height: 0.9em;
+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
+28 -24
View File
@@ -661,32 +661,36 @@ def get_setting(key: str, default: Any = None) -> Any:
Returns:
Setting value (auto-converted from JSON for complex types)
"""
with get_db() as conn:
cursor = conn.execute(
'SELECT value, value_type FROM settings WHERE key = ?',
(key,)
)
row = cursor.fetchone()
try:
with get_db() as conn:
cursor = conn.execute(
'SELECT value, value_type FROM settings WHERE key = ?',
(key,)
)
row = cursor.fetchone()
if row is None:
return default
value, value_type = row['value'], row['value_type']
# Convert based on type
if value_type == 'json':
try:
return json.loads(value)
except json.JSONDecodeError:
if row is None:
return default
elif value_type == 'int':
return int(value)
elif value_type == 'float':
return float(value)
elif value_type == 'bool':
return value.lower() in ('true', '1', 'yes')
else:
return value
value, value_type = row['value'], row['value_type']
# Convert based on type
if value_type == 'json':
try:
return json.loads(value)
except json.JSONDecodeError:
return default
elif value_type == 'int':
return int(value)
elif value_type == 'float':
return float(value)
elif value_type == 'bool':
return value.lower() in ('true', '1', 'yes')
else:
return value
except sqlite3.OperationalError:
logger.warning("Database unavailable reading setting '%s', using default", key)
return default
def set_setting(key: str, value: Any) -> None:
+36 -4
View File
@@ -46,6 +46,35 @@ def _rtl_tool_supports_bias_t(tool_path: str) -> bool:
return False
def enable_bias_t_via_rtl_biast(device_index: int = 0) -> bool:
"""Enable bias-t power using rtl_biast (RTL-SDR Blog drivers).
Runs rtl_biast to set the bias-t register on the device, then exits.
The setting persists across device opens until the device is reset.
Returns True if bias-t was enabled successfully.
"""
rtl_biast_path = get_tool_path('rtl_biast') or 'rtl_biast'
try:
result = subprocess.run(
[rtl_biast_path, '-b', '1', '-d', str(device_index)],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
logger.info(f"Bias-t enabled via rtl_biast on device {device_index}")
return True
logger.warning(f"rtl_biast failed (exit {result.returncode}): {result.stderr.strip()}")
return False
except FileNotFoundError:
logger.warning("rtl_biast not found — install RTL-SDR Blog drivers for bias-t support")
return False
except Exception as e:
logger.warning(f"Failed to enable bias-t via rtl_biast: {e}")
return False
def _get_dump1090_bias_t_flag(dump1090_path: str) -> str | None:
"""Detect the correct bias-t flag for the installed dump1090 variant.
@@ -197,10 +226,13 @@ class RTLSDRCommandBuilder(CommandBuilder):
if bias_t_flag:
cmd.append(bias_t_flag)
else:
logger.warning(
f"Bias-t requested but {dump1090_path} does not support it. "
"Consider using dump1090-fa or readsb for bias-t support."
)
# Fallback: use rtl_biast to set bias-t before starting dump1090
if not enable_bias_t_via_rtl_biast(device.index):
logger.warning(
f"Bias-t requested but {dump1090_path} does not support it "
"and rtl_biast is not available. Install RTL-SDR Blog drivers "
"or use dump1090-fa/readsb for bias-t support."
)
return cmd