mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Single-threaded make on a Raspberry Pi 5 could take 5-10+ minutes with no output, making the setup appear hung. Now uses all available CPU cores and prints a "still compiling" heartbeat every 20s. Also prints build log tail on failure for easier debugging. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1681 lines
53 KiB
Bash
Executable File
1681 lines
53 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# INTERCEPT Setup Script (best-effort installs, hard-fail verification)
|
|
|
|
# ---- Force bash even if launched with sh ----
|
|
if [ -z "${BASH_VERSION:-}" ]; then
|
|
echo "[x] This script must be run with bash (not sh)."
|
|
echo " Run: bash $0"
|
|
exec bash "$0" "$@"
|
|
fi
|
|
|
|
set -Eeuo pipefail
|
|
|
|
# Ensure admin paths are searchable (many tools live here)
|
|
export PATH="/usr/local/sbin:/usr/sbin:/sbin:/opt/homebrew/sbin:/opt/homebrew/bin:$PATH"
|
|
|
|
# ----------------------------
|
|
# Pretty output
|
|
# ----------------------------
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
info() { echo -e "${BLUE}[*]${NC} $*"; }
|
|
ok() { echo -e "${GREEN}[✓]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
|
fail() { echo -e "${RED}[x]${NC} $*"; }
|
|
|
|
# ----------------------------
|
|
# Progress tracking
|
|
# ----------------------------
|
|
CURRENT_STEP=0
|
|
TOTAL_STEPS=0
|
|
|
|
progress() {
|
|
local msg="$1"
|
|
((CURRENT_STEP++)) || true
|
|
local pct=$((CURRENT_STEP * 100 / TOTAL_STEPS))
|
|
local filled=$((pct / 5))
|
|
local empty=$((20 - filled))
|
|
local bar=$(printf '█%.0s' $(seq 1 $filled 2>/dev/null) || true)
|
|
bar+=$(printf '░%.0s' $(seq 1 $empty 2>/dev/null) || true)
|
|
echo -e "${BLUE}[${CURRENT_STEP}/${TOTAL_STEPS}]${NC} ${bar} ${pct}% - ${msg}"
|
|
}
|
|
|
|
on_error() {
|
|
local line="$1"
|
|
local cmd="${2:-unknown}"
|
|
fail "Setup failed at line ${line}: ${cmd}"
|
|
exit 1
|
|
}
|
|
trap 'on_error $LINENO "$BASH_COMMAND"' ERR
|
|
|
|
# ----------------------------
|
|
# Banner
|
|
# ----------------------------
|
|
echo -e "${BLUE}"
|
|
echo " ___ _ _ _____ _____ ____ ____ _____ ____ _____ "
|
|
echo " |_ _| \\ | |_ _| ____| _ \\ / ___| ____| _ \\_ _|"
|
|
echo " | || \\| | | | | _| | |_) | | | _| | |_) || | "
|
|
echo " | || |\\ | | | | |___| _ <| |___| |___| __/ | | "
|
|
echo " |___|_| \\_| |_| |_____|_| \\_\\\\____|_____|_| |_| "
|
|
echo -e "${NC}"
|
|
echo "INTERCEPT - Setup Script"
|
|
echo "============================================"
|
|
echo
|
|
|
|
# ----------------------------
|
|
# Helpers
|
|
# ----------------------------
|
|
NON_INTERACTIVE=false
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--non-interactive)
|
|
NON_INTERACTIVE=true
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
done
|
|
|
|
cmd_exists() {
|
|
local c="$1"
|
|
command -v "$c" >/dev/null 2>&1 && return 0
|
|
[[ -x "/usr/sbin/$c" || -x "/sbin/$c" || -x "/usr/local/sbin/$c" || -x "/opt/homebrew/sbin/$c" ]] && return 0
|
|
return 1
|
|
}
|
|
|
|
ask_yes_no() {
|
|
local prompt="$1"
|
|
local default="${2:-n}" # default to no for safety
|
|
local response
|
|
|
|
if $NON_INTERACTIVE; then
|
|
info "Non-interactive mode: defaulting to ${default} for prompt: ${prompt}"
|
|
[[ "$default" == "y" ]]
|
|
return
|
|
fi
|
|
|
|
if [[ ! -t 0 ]]; then
|
|
warn "No TTY available for prompt: ${prompt}"
|
|
[[ "$default" == "y" ]]
|
|
return
|
|
fi
|
|
|
|
if [[ "$default" == "y" ]]; then
|
|
read -r -p "$prompt [Y/n]: " response
|
|
[[ -z "$response" || "$response" =~ ^[Yy] ]]
|
|
else
|
|
read -r -p "$prompt [y/N]: " response
|
|
[[ "$response" =~ ^[Yy] ]]
|
|
fi
|
|
}
|
|
|
|
have_any() {
|
|
local c
|
|
for c in "$@"; do
|
|
cmd_exists "$c" && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
need_sudo() {
|
|
if [[ "$(id -u)" -eq 0 ]]; then
|
|
SUDO=""
|
|
ok "Running as root"
|
|
else
|
|
if cmd_exists sudo; then
|
|
SUDO="sudo"
|
|
else
|
|
fail "sudo is not installed and you're not root."
|
|
echo "Either run as root or install sudo first."
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Refresh sudo credential cache so long-running builds don't trigger
|
|
# mid-compilation password prompts (which can fail due to TTY issues
|
|
# inside subshells). Safe to call multiple times.
|
|
refresh_sudo() {
|
|
[[ -z "${SUDO:-}" ]] && return 0
|
|
sudo -v 2>/dev/null || true
|
|
}
|
|
|
|
detect_os() {
|
|
if [[ "${OSTYPE:-}" == "darwin"* ]]; then
|
|
OS="macos"
|
|
elif [[ -f /etc/debian_version ]]; then
|
|
OS="debian"
|
|
else
|
|
OS="unknown"
|
|
fi
|
|
info "Detected OS: ${OS}"
|
|
[[ "$OS" != "unknown" ]] || { fail "Unsupported OS (macOS + Debian/Ubuntu only)."; exit 1; }
|
|
}
|
|
|
|
detect_dragonos() {
|
|
IS_DRAGONOS=false
|
|
# Check for DragonOS markers
|
|
if [[ -f /etc/dragonos-release ]] || \
|
|
[[ -d /usr/share/dragonos ]] || \
|
|
grep -qi "dragonos" /etc/os-release 2>/dev/null; then
|
|
IS_DRAGONOS=true
|
|
warn "DragonOS detected! This distro has many tools pre-installed."
|
|
warn "The script will prompt before making system changes."
|
|
fi
|
|
}
|
|
|
|
# ----------------------------
|
|
# Required tool checks (with alternates)
|
|
# ----------------------------
|
|
missing_required=()
|
|
missing_recommended=()
|
|
|
|
check_required() {
|
|
local label="$1"; shift
|
|
local desc="$1"; shift
|
|
|
|
if have_any "$@"; then
|
|
ok "${label} - ${desc}"
|
|
else
|
|
warn "${label} - ${desc} (missing, required)"
|
|
missing_required+=("$label")
|
|
fi
|
|
}
|
|
|
|
check_recommended() {
|
|
local label="$1"; shift
|
|
local desc="$1"; shift
|
|
|
|
if have_any "$@"; then
|
|
ok "${label} - ${desc}"
|
|
else
|
|
warn "${label} - ${desc} (missing, recommended)"
|
|
missing_recommended+=("$label")
|
|
fi
|
|
}
|
|
|
|
check_optional() {
|
|
local label="$1"; shift
|
|
local desc="$1"; shift
|
|
|
|
if have_any "$@"; then
|
|
ok "${label} - ${desc}"
|
|
else
|
|
warn "${label} - ${desc} (missing, optional)"
|
|
fi
|
|
}
|
|
|
|
check_tools() {
|
|
info "Checking required tools..."
|
|
missing_required=()
|
|
|
|
echo
|
|
info "Core SDR:"
|
|
check_required "rtl_fm" "RTL-SDR FM demodulator" rtl_fm
|
|
check_required "rtl_test" "RTL-SDR device detection" rtl_test
|
|
check_required "rtl_tcp" "RTL-SDR TCP server" rtl_tcp
|
|
check_required "multimon-ng" "Pager decoder" multimon-ng
|
|
check_required "rtl_433" "433MHz sensor decoder" rtl_433 rtl433
|
|
check_optional "rtlamr" "Utility meter decoder (requires Go)" rtlamr
|
|
check_optional "hackrf_transfer" "HackRF SubGHz transceiver" hackrf_transfer
|
|
check_optional "hackrf_sweep" "HackRF spectrum analyzer" hackrf_sweep
|
|
check_required "dump1090" "ADS-B decoder" dump1090
|
|
check_required "acarsdec" "ACARS decoder" acarsdec
|
|
check_optional "dumpvdl2" "VDL2 decoder" dumpvdl2
|
|
check_required "AIS-catcher" "AIS vessel decoder" AIS-catcher aiscatcher
|
|
check_optional "satdump" "Weather satellite decoder (NOAA/Meteor)" satdump
|
|
echo
|
|
info "GPS:"
|
|
check_required "gpsd" "GPS daemon" gpsd
|
|
|
|
echo
|
|
info "Digital Voice:"
|
|
check_optional "dsd" "Digital Speech Decoder (DMR/P25)" dsd dsd-fme
|
|
|
|
echo
|
|
info "Audio:"
|
|
check_required "ffmpeg" "Audio encoder/decoder" ffmpeg
|
|
|
|
echo
|
|
info "WiFi:"
|
|
check_required "airmon-ng" "Monitor mode helper" airmon-ng
|
|
check_required "airodump-ng" "WiFi scanner" airodump-ng
|
|
check_required "aireplay-ng" "Injection/deauth" aireplay-ng
|
|
check_required "hcxdumptool" "PMKID capture" hcxdumptool
|
|
check_required "hcxpcapngtool" "PMKID/pcapng conversion" hcxpcapngtool
|
|
|
|
echo
|
|
info "Bluetooth:"
|
|
check_required "bluetoothctl" "Bluetooth controller CLI" bluetoothctl
|
|
check_required "hcitool" "Bluetooth scan utility" hcitool
|
|
check_required "hciconfig" "Bluetooth adapter config" hciconfig
|
|
|
|
echo
|
|
info "SoapySDR:"
|
|
check_required "SoapySDRUtil" "SoapySDR CLI utility" SoapySDRUtil
|
|
echo
|
|
}
|
|
|
|
# ----------------------------
|
|
# Python venv + deps
|
|
# ----------------------------
|
|
check_python_version() {
|
|
if ! cmd_exists python3; then
|
|
fail "python3 not found."
|
|
[[ "$OS" == "macos" ]] && echo "Install with: brew install python"
|
|
[[ "$OS" == "debian" ]] && echo "Install with: sudo apt-get install python3"
|
|
exit 1
|
|
fi
|
|
|
|
local ver
|
|
ver="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
|
|
info "Python version: ${ver}"
|
|
|
|
python3 - <<'PY'
|
|
import sys
|
|
raise SystemExit(0 if sys.version_info >= (3,9) else 1)
|
|
PY
|
|
ok "Python version OK (>= 3.9)"
|
|
}
|
|
|
|
install_python_deps() {
|
|
progress "Setting up Python environment"
|
|
check_python_version
|
|
|
|
if [[ ! -f requirements.txt ]]; then
|
|
warn "requirements.txt not found; skipping Python dependency install."
|
|
return 0
|
|
fi
|
|
|
|
# On Debian/Ubuntu, try apt packages first as they're more reliable
|
|
if [[ "$OS" == "debian" ]]; then
|
|
info "Installing Python packages via apt (more reliable on Debian/Ubuntu)..."
|
|
$SUDO apt-get install -y python3-flask python3-requests python3-serial >/dev/null 2>&1 || true
|
|
|
|
# skyfield may not be available in all distros, try apt first then pip
|
|
if ! $SUDO apt-get install -y python3-skyfield >/dev/null 2>&1; then
|
|
warn "python3-skyfield not in apt, will try pip later"
|
|
fi
|
|
ok "Installed available Python packages via apt"
|
|
fi
|
|
|
|
if [[ ! -d venv ]]; then
|
|
python3 -m venv --system-site-packages venv
|
|
ok "Created venv/ (with system site-packages)"
|
|
else
|
|
ok "Using existing venv/"
|
|
fi
|
|
|
|
# shellcheck disable=SC1091
|
|
source venv/bin/activate
|
|
local PIP="venv/bin/python -m pip"
|
|
local PY="venv/bin/python"
|
|
|
|
$PIP install --upgrade pip setuptools wheel >/dev/null 2>&1 || true
|
|
ok "Upgraded pip tooling"
|
|
|
|
progress "Installing Python dependencies"
|
|
|
|
# Install critical packages first to avoid all-or-nothing failures
|
|
# (C extension packages like scipy/numpy can fail on newer Python versions
|
|
# and cause pip to roll back pure-Python packages like flask)
|
|
info "Installing core packages..."
|
|
$PIP install "flask>=3.0.0" "flask-limiter>=2.5.4" "requests>=2.28.0" \
|
|
"Werkzeug>=3.1.5" "pyserial>=3.5" "flask-sock" "websocket-client>=1.6.0" 2>&1 \
|
|
| tail -5 || true
|
|
|
|
# Verify critical packages
|
|
$PY -c "import flask; import requests; from flask_limiter import Limiter" 2>/dev/null || {
|
|
fail "Critical Python packages (flask, requests, flask-limiter) not installed"
|
|
echo "Try: venv/bin/pip install flask requests flask-limiter"
|
|
exit 1
|
|
}
|
|
ok "Core Python packages installed"
|
|
|
|
# Install optional packages individually (some may fail on newer Python)
|
|
info "Installing optional packages..."
|
|
for pkg in "numpy>=1.24.0" "scipy>=1.10.0" "Pillow>=9.0.0" "skyfield>=1.45" \
|
|
"bleak>=0.21.0" "psycopg2-binary>=2.9.9" "meshtastic>=2.0.0" \
|
|
"scapy>=2.4.5" "qrcode[pil]>=7.4" "cryptography>=41.0.0"; do
|
|
pkg_name="${pkg%%>=*}"
|
|
if ! $PIP install "$pkg" 2>/dev/null; then
|
|
warn "${pkg_name} failed to install (optional - related features may be unavailable)"
|
|
fi
|
|
done
|
|
ok "Optional packages processed"
|
|
echo
|
|
}
|
|
|
|
# ----------------------------
|
|
# macOS install (Homebrew)
|
|
# ----------------------------
|
|
ensure_brew() {
|
|
cmd_exists brew && return 0
|
|
warn "Homebrew not found. Installing Homebrew..."
|
|
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
|
|
if [[ -x /opt/homebrew/bin/brew ]]; then
|
|
eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
elif [[ -x /usr/local/bin/brew ]]; then
|
|
eval "$(/usr/local/bin/brew shellenv)"
|
|
fi
|
|
|
|
cmd_exists brew || { fail "Homebrew install failed. Install manually then re-run."; exit 1; }
|
|
}
|
|
|
|
brew_install() {
|
|
local pkg="$1"
|
|
if brew list --formula "$pkg" >/dev/null 2>&1; then
|
|
ok "brew: ${pkg} already installed"
|
|
return 0
|
|
fi
|
|
info "brew: installing ${pkg}..."
|
|
if brew install "$pkg" 2>&1; then
|
|
ok "brew: installed ${pkg}"
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
install_rtlamr_from_source() {
|
|
info "Installing rtlamr from source (requires Go)..."
|
|
|
|
# Check if Go is installed, install if needed
|
|
if ! cmd_exists go; then
|
|
if [[ "$OS" == "macos" ]]; then
|
|
info "Installing Go via Homebrew..."
|
|
brew_install go || { warn "Failed to install Go. Cannot install rtlamr."; return 1; }
|
|
else
|
|
info "Installing Go via apt..."
|
|
$SUDO apt-get install -y golang >/dev/null 2>&1 || { warn "Failed to install Go. Cannot install rtlamr."; return 1; }
|
|
fi
|
|
fi
|
|
|
|
# Set up Go environment
|
|
export GOPATH="${GOPATH:-$HOME/go}"
|
|
export PATH="$GOPATH/bin:$PATH"
|
|
mkdir -p "$GOPATH/bin"
|
|
|
|
info "Building rtlamr..."
|
|
if go install github.com/bemasher/rtlamr@latest 2>/dev/null; then
|
|
# Link to system path
|
|
if [[ -f "$GOPATH/bin/rtlamr" ]]; then
|
|
if [[ "$OS" == "macos" ]]; then
|
|
if [[ -w /usr/local/bin ]]; then
|
|
ln -sf "$GOPATH/bin/rtlamr" /usr/local/bin/rtlamr
|
|
else
|
|
$SUDO ln -sf "$GOPATH/bin/rtlamr" /usr/local/bin/rtlamr
|
|
fi
|
|
else
|
|
$SUDO ln -sf "$GOPATH/bin/rtlamr" /usr/local/bin/rtlamr
|
|
fi
|
|
ok "rtlamr installed successfully"
|
|
else
|
|
warn "rtlamr binary not found after build"
|
|
return 1
|
|
fi
|
|
else
|
|
warn "Failed to build rtlamr"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
|
|
install_multimon_ng_from_source_macos() {
|
|
info "multimon-ng not available via Homebrew. Building from source..."
|
|
|
|
# Ensure build dependencies are installed
|
|
brew_install cmake
|
|
brew_install libsndfile
|
|
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning multimon-ng..."
|
|
git clone --depth 1 https://github.com/EliasOenal/multimon-ng.git "$tmp_dir/multimon-ng" >/dev/null 2>&1 \
|
|
|| { fail "Failed to clone multimon-ng"; exit 1; }
|
|
|
|
cd "$tmp_dir/multimon-ng"
|
|
info "Compiling multimon-ng..."
|
|
mkdir -p build && cd build
|
|
cmake .. >/dev/null 2>&1 || { fail "cmake failed for multimon-ng"; exit 1; }
|
|
make >/dev/null 2>&1 || { fail "make failed for multimon-ng"; exit 1; }
|
|
|
|
# Install to /usr/local/bin (no sudo needed on Homebrew systems typically)
|
|
if [[ -w /usr/local/bin ]]; then
|
|
install -m 0755 multimon-ng /usr/local/bin/multimon-ng
|
|
else
|
|
refresh_sudo
|
|
$SUDO install -m 0755 multimon-ng /usr/local/bin/multimon-ng
|
|
fi
|
|
ok "multimon-ng installed successfully from source"
|
|
)
|
|
}
|
|
|
|
install_dsd_from_source() {
|
|
info "Building DSD (Digital Speech Decoder) from source..."
|
|
info "This requires mbelib (vocoder library) as a prerequisite."
|
|
|
|
if [[ "$OS" == "macos" ]]; then
|
|
brew_install cmake
|
|
brew_install libsndfile
|
|
brew_install ncurses
|
|
brew_install fftw
|
|
brew_install codec2
|
|
brew_install librtlsdr
|
|
brew_install pulseaudio || true
|
|
else
|
|
apt_install build-essential git cmake libsndfile1-dev libpulse-dev \
|
|
libfftw3-dev liblapack-dev libncurses-dev librtlsdr-dev libcodec2-dev
|
|
fi
|
|
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
# Step 1: Build and install mbelib (required dependency)
|
|
info "Building mbelib (vocoder library)..."
|
|
git clone https://github.com/lwvmobile/mbelib.git "$tmp_dir/mbelib" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone mbelib"; exit 1; }
|
|
|
|
cd "$tmp_dir/mbelib"
|
|
git checkout ambe_tones >/dev/null 2>&1 || true
|
|
mkdir -p build && cd build
|
|
|
|
if cmake .. >/dev/null 2>&1 && make -j "$(nproc 2>/dev/null || sysctl -n hw.ncpu)" >/dev/null 2>&1; then
|
|
if [[ "$OS" == "macos" ]]; then
|
|
if [[ -w /usr/local/lib ]]; then
|
|
make install >/dev/null 2>&1
|
|
else
|
|
refresh_sudo
|
|
$SUDO make install >/dev/null 2>&1
|
|
fi
|
|
else
|
|
$SUDO make install >/dev/null 2>&1
|
|
$SUDO ldconfig 2>/dev/null || true
|
|
fi
|
|
ok "mbelib installed"
|
|
else
|
|
warn "Failed to build mbelib. Cannot build DSD without it."
|
|
exit 1
|
|
fi
|
|
|
|
# Step 2: Build dsd-fme (or fall back to original dsd)
|
|
info "Building dsd-fme..."
|
|
git clone --depth 1 https://github.com/lwvmobile/dsd-fme.git "$tmp_dir/dsd-fme" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone dsd-fme, trying original DSD...";
|
|
git clone --depth 1 https://github.com/szechyjs/dsd.git "$tmp_dir/dsd-fme" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone DSD"; exit 1; }; }
|
|
|
|
cd "$tmp_dir/dsd-fme"
|
|
mkdir -p build && cd build
|
|
|
|
# On macOS, help cmake find Homebrew ncurses
|
|
local cmake_flags=""
|
|
if [[ "$OS" == "macos" ]]; then
|
|
local ncurses_prefix
|
|
ncurses_prefix="$(brew --prefix ncurses 2>/dev/null || echo /opt/homebrew/opt/ncurses)"
|
|
cmake_flags="-DCMAKE_PREFIX_PATH=$ncurses_prefix"
|
|
fi
|
|
|
|
info "Compiling DSD..."
|
|
if cmake .. $cmake_flags >/dev/null 2>&1 && make -j "$(nproc 2>/dev/null || sysctl -n hw.ncpu)" >/dev/null 2>&1; then
|
|
if [[ "$OS" == "macos" ]]; then
|
|
if [[ -w /usr/local/bin ]]; then
|
|
install -m 0755 dsd-fme /usr/local/bin/dsd 2>/dev/null || install -m 0755 dsd /usr/local/bin/dsd 2>/dev/null || true
|
|
else
|
|
refresh_sudo
|
|
$SUDO install -m 0755 dsd-fme /usr/local/bin/dsd 2>/dev/null || $SUDO install -m 0755 dsd /usr/local/bin/dsd 2>/dev/null || true
|
|
fi
|
|
else
|
|
$SUDO make install >/dev/null 2>&1 \
|
|
|| $SUDO install -m 0755 dsd-fme /usr/local/bin/dsd 2>/dev/null \
|
|
|| $SUDO install -m 0755 dsd /usr/local/bin/dsd 2>/dev/null \
|
|
|| true
|
|
$SUDO ldconfig 2>/dev/null || true
|
|
fi
|
|
ok "DSD installed successfully"
|
|
else
|
|
warn "Failed to build DSD from source. DMR/P25 decoding will not be available."
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_dump1090_from_source_macos() {
|
|
info "dump1090 not available via Homebrew. Building from source..."
|
|
|
|
brew_install cmake
|
|
brew_install librtlsdr
|
|
brew_install pkg-config
|
|
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning FlightAware dump1090..."
|
|
git clone --depth 1 https://github.com/flightaware/dump1090.git "$tmp_dir/dump1090" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone dump1090"; exit 1; }
|
|
|
|
cd "$tmp_dir/dump1090"
|
|
sed -i '' 's/-Werror//g' Makefile 2>/dev/null || true
|
|
info "Compiling dump1090..."
|
|
if make BLADERF=no RTLSDR=yes 2>&1 | tail -5; then
|
|
if [[ -w /usr/local/bin ]]; then
|
|
install -m 0755 dump1090 /usr/local/bin/dump1090
|
|
else
|
|
refresh_sudo
|
|
$SUDO install -m 0755 dump1090 /usr/local/bin/dump1090
|
|
fi
|
|
ok "dump1090 installed successfully from source"
|
|
else
|
|
warn "Failed to build dump1090. ADS-B decoding will not be available."
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_acarsdec_from_source_macos() {
|
|
info "acarsdec not available via Homebrew. Building from source..."
|
|
|
|
brew_install cmake
|
|
brew_install librtlsdr
|
|
brew_install libsndfile
|
|
brew_install pkg-config
|
|
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning acarsdec..."
|
|
git clone --depth 1 https://github.com/TLeconte/acarsdec.git "$tmp_dir/acarsdec" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone acarsdec"; exit 1; }
|
|
|
|
cd "$tmp_dir/acarsdec"
|
|
|
|
# Fix compiler flags for macOS Apple Silicon (ARM64)
|
|
# -march=native can fail with Apple Clang on M-series chips
|
|
# -Ofast is deprecated in modern Clang
|
|
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)"
|
|
fi
|
|
|
|
# Fix pthread_tryjoin_np (Linux-only GNU extension) for macOS
|
|
# Replace with pthread_join which provides equivalent behavior
|
|
if grep -q 'pthread_tryjoin_np' rtl.c 2>/dev/null; then
|
|
sed -i '' 's/pthread_tryjoin_np(\([^,]*\), NULL)/pthread_join(\1, NULL)/g' rtl.c
|
|
info "Patched pthread_tryjoin_np for macOS compatibility"
|
|
fi
|
|
|
|
# Fix libacars linking on macOS (upstream issue #112)
|
|
# Use LIBACARS_LINK_LIBRARIES (full path) instead of LIBACARS_LIBRARIES (name only)
|
|
if grep -q 'LIBACARS_LIBRARIES' CMakeLists.txt 2>/dev/null; then
|
|
sed -i '' 's/${LIBACARS_LIBRARIES}/${LIBACARS_LINK_LIBRARIES}/g' CMakeLists.txt
|
|
info "Patched libacars linking for macOS"
|
|
fi
|
|
|
|
mkdir -p build && cd build
|
|
|
|
# Set Homebrew paths for Apple Silicon (/opt/homebrew) or Intel (/usr/local)
|
|
HOMEBREW_PREFIX="$(brew --prefix)"
|
|
export PKG_CONFIG_PATH="${HOMEBREW_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
|
|
export CMAKE_PREFIX_PATH="${HOMEBREW_PREFIX}"
|
|
|
|
info "Compiling acarsdec..."
|
|
build_log="$tmp_dir/acarsdec-build.log"
|
|
if cmake .. -Drtl=ON \
|
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
|
-DCMAKE_C_FLAGS="-I${HOMEBREW_PREFIX}/include" \
|
|
-DCMAKE_EXE_LINKER_FLAGS="-L${HOMEBREW_PREFIX}/lib" \
|
|
>"$build_log" 2>&1 \
|
|
&& make >>"$build_log" 2>&1; then
|
|
if [[ -w /usr/local/bin ]]; then
|
|
install -m 0755 acarsdec /usr/local/bin/acarsdec
|
|
else
|
|
refresh_sudo
|
|
$SUDO install -m 0755 acarsdec /usr/local/bin/acarsdec
|
|
fi
|
|
ok "acarsdec installed successfully from source"
|
|
else
|
|
warn "Failed to build acarsdec. ACARS decoding will not be available."
|
|
warn "Build log (last 30 lines):"
|
|
tail -30 "$build_log" | while IFS= read -r line; do warn " $line"; done
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_dumpvdl2_from_source_macos() {
|
|
info "Building dumpvdl2 from source (with libacars dependency)..."
|
|
|
|
brew_install cmake
|
|
brew_install librtlsdr
|
|
brew_install pkg-config
|
|
brew_install glib
|
|
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
HOMEBREW_PREFIX="$(brew --prefix)"
|
|
export PKG_CONFIG_PATH="${HOMEBREW_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
|
|
export CMAKE_PREFIX_PATH="${HOMEBREW_PREFIX}"
|
|
|
|
# Build libacars first
|
|
info "Cloning libacars..."
|
|
git clone --depth 1 https://github.com/szpajder/libacars.git "$tmp_dir/libacars" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone libacars"; exit 1; }
|
|
|
|
cd "$tmp_dir/libacars"
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling libacars..."
|
|
build_log="$tmp_dir/libacars-build.log"
|
|
if cmake .. \
|
|
-DCMAKE_C_FLAGS="-I${HOMEBREW_PREFIX}/include" \
|
|
-DCMAKE_EXE_LINKER_FLAGS="-L${HOMEBREW_PREFIX}/lib" \
|
|
>"$build_log" 2>&1 \
|
|
&& make >>"$build_log" 2>&1; then
|
|
if [[ -w /usr/local/lib ]]; then
|
|
make install >>"$build_log" 2>&1
|
|
else
|
|
refresh_sudo
|
|
$SUDO make install >>"$build_log" 2>&1
|
|
fi
|
|
ok "libacars installed"
|
|
else
|
|
warn "Failed to build libacars."
|
|
tail -20 "$build_log" | while IFS= read -r line; do warn " $line"; done
|
|
exit 1
|
|
fi
|
|
|
|
# Build dumpvdl2
|
|
info "Cloning dumpvdl2..."
|
|
git clone --depth 1 https://github.com/szpajder/dumpvdl2.git "$tmp_dir/dumpvdl2" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone dumpvdl2"; exit 1; }
|
|
|
|
cd "$tmp_dir/dumpvdl2"
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling dumpvdl2..."
|
|
build_log="$tmp_dir/dumpvdl2-build.log"
|
|
if cmake .. \
|
|
-DCMAKE_C_FLAGS="-I${HOMEBREW_PREFIX}/include" \
|
|
-DCMAKE_EXE_LINKER_FLAGS="-L${HOMEBREW_PREFIX}/lib" \
|
|
>"$build_log" 2>&1 \
|
|
&& make >>"$build_log" 2>&1; then
|
|
if [[ -w /usr/local/bin ]]; then
|
|
install -m 0755 src/dumpvdl2 /usr/local/bin/dumpvdl2
|
|
else
|
|
refresh_sudo
|
|
$SUDO install -m 0755 src/dumpvdl2 /usr/local/bin/dumpvdl2
|
|
fi
|
|
ok "dumpvdl2 installed successfully from source"
|
|
else
|
|
warn "Failed to build dumpvdl2. VDL2 decoding will not be available."
|
|
warn "Build log (last 30 lines):"
|
|
tail -30 "$build_log" | while IFS= read -r line; do warn " $line"; done
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_aiscatcher_from_source_macos() {
|
|
info "AIS-catcher not available via Homebrew. Building from source..."
|
|
|
|
brew_install cmake
|
|
brew_install librtlsdr
|
|
brew_install curl
|
|
brew_install pkg-config
|
|
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning AIS-catcher..."
|
|
git clone --depth 1 https://github.com/jvde-github/AIS-catcher.git "$tmp_dir/AIS-catcher" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone AIS-catcher"; exit 1; }
|
|
|
|
cd "$tmp_dir/AIS-catcher"
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling AIS-catcher..."
|
|
if cmake .. >/dev/null 2>&1 && make >/dev/null 2>&1; then
|
|
if [[ -w /usr/local/bin ]]; then
|
|
install -m 0755 AIS-catcher /usr/local/bin/AIS-catcher
|
|
else
|
|
refresh_sudo
|
|
$SUDO install -m 0755 AIS-catcher /usr/local/bin/AIS-catcher
|
|
fi
|
|
ok "AIS-catcher installed successfully from source"
|
|
else
|
|
warn "Failed to build AIS-catcher. AIS vessel tracking will not be available."
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_satdump_from_source_debian() {
|
|
info "Building SatDump v1.2.2 from source (weather satellite decoder)..."
|
|
|
|
apt_install build-essential git cmake pkg-config \
|
|
libpng-dev libtiff-dev libjemalloc-dev libvolk-dev libnng-dev \
|
|
libzstd-dev libsoapysdr-dev libhackrf-dev liblimesuite-dev \
|
|
libsqlite3-dev libcurl4-openssl-dev zlib1g-dev libzmq3-dev libfftw3-dev
|
|
|
|
# Run in subshell to isolate EXIT trap
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning SatDump v1.2.2..."
|
|
git clone --depth 1 --branch 1.2.2 https://github.com/SatDump/SatDump.git "$tmp_dir/SatDump" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone SatDump"; exit 1; }
|
|
|
|
cd "$tmp_dir/SatDump"
|
|
|
|
# Patch: fix deprecated std::allocator usage for newer compilers
|
|
# GCC 13+ errors on deprecated allocator members in sol2.
|
|
# Pragmas must go in lua_utils.cpp (the instantiation site), not sol.hpp (definition site).
|
|
lua_utils="src-core/common/lua/lua_utils.cpp"
|
|
if [ -f "$lua_utils" ]; then
|
|
{
|
|
echo '#pragma GCC diagnostic push'
|
|
echo '#pragma GCC diagnostic ignored "-Wdeprecated"'
|
|
echo '#pragma GCC diagnostic ignored "-Wdeprecated-declarations"'
|
|
cat "$lua_utils"
|
|
echo '#pragma GCC diagnostic pop'
|
|
} > "${lua_utils}.patched" && mv "${lua_utils}.patched" "$lua_utils"
|
|
fi
|
|
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling SatDump (this is a large C++ project and may take 10-30 minutes)..."
|
|
build_log="$tmp_dir/satdump-build.log"
|
|
|
|
# Show periodic progress while building so the user knows it's not hung
|
|
(
|
|
while true; do
|
|
sleep 30
|
|
if [ -f "$build_log" ]; then
|
|
local_lines=$(wc -l < "$build_log" 2>/dev/null || echo 0)
|
|
printf " [*] Still compiling SatDump... (%s lines of build output so far)\n" "$local_lines"
|
|
fi
|
|
done
|
|
) &
|
|
progress_pid=$!
|
|
|
|
if cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_GUI=OFF -DCMAKE_INSTALL_LIBDIR=lib .. >"$build_log" 2>&1 \
|
|
&& make -j "$(nproc)" >>"$build_log" 2>&1; then
|
|
kill $progress_pid 2>/dev/null; wait $progress_pid 2>/dev/null
|
|
$SUDO make install >/dev/null 2>&1
|
|
$SUDO ldconfig
|
|
|
|
# Ensure plugins are in the expected path (handles multiarch differences)
|
|
$SUDO mkdir -p /usr/local/lib/satdump/plugins
|
|
if [ -z "$(ls /usr/local/lib/satdump/plugins/*.so 2>/dev/null)" ]; then
|
|
for dir in /usr/local/lib/*/satdump/plugins /usr/lib/*/satdump/plugins /usr/lib/satdump/plugins; do
|
|
if [ -d "$dir" ] && [ -n "$(ls "$dir"/*.so 2>/dev/null)" ]; then
|
|
$SUDO ln -sf "$dir"/*.so /usr/local/lib/satdump/plugins/
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
ok "SatDump installed successfully."
|
|
else
|
|
kill $progress_pid 2>/dev/null; wait $progress_pid 2>/dev/null
|
|
warn "Failed to build SatDump from source. Weather satellite decoding will not be available."
|
|
warn "Build log (last 30 lines):"
|
|
tail -30 "$build_log" | while IFS= read -r line; do warn " $line"; done
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_satdump_macos() {
|
|
info "Installing SatDump v1.2.2 from pre-built release (weather satellite decoder)..."
|
|
|
|
# Determine architecture
|
|
local arch
|
|
arch="$(uname -m)"
|
|
local dmg_name
|
|
if [ "$arch" = "arm64" ]; then
|
|
dmg_name="SatDump-macOS-Silicon.dmg"
|
|
else
|
|
dmg_name="SatDump-macOS-Intel.dmg"
|
|
fi
|
|
|
|
local dmg_url="https://github.com/SatDump/SatDump/releases/download/1.2.2/${dmg_name}"
|
|
local install_dir="/usr/local/lib/satdump"
|
|
|
|
# Run in subshell to isolate EXIT trap
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'hdiutil detach "$tmp_dir/mnt" -quiet 2>/dev/null || true; rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Downloading ${dmg_name}..."
|
|
if ! curl -sL -o "$tmp_dir/satdump.dmg" "$dmg_url"; then
|
|
warn "Failed to download SatDump. Weather satellite decoding will not be available."
|
|
exit 1
|
|
fi
|
|
|
|
info "Installing SatDump..."
|
|
# Mount the DMG
|
|
hdiutil attach "$tmp_dir/satdump.dmg" -nobrowse -quiet -mountpoint "$tmp_dir/mnt" \
|
|
|| { warn "Failed to mount SatDump DMG"; exit 1; }
|
|
|
|
local app_dir="$tmp_dir/mnt/SatDump.app"
|
|
if [ ! -d "$app_dir" ]; then
|
|
warn "SatDump.app not found in DMG"
|
|
exit 1
|
|
fi
|
|
|
|
# Install: copy app contents to /usr/local/lib/satdump
|
|
refresh_sudo
|
|
$SUDO mkdir -p "$install_dir"
|
|
$SUDO cp -R "$app_dir/Contents/MacOS/"* "$install_dir/"
|
|
$SUDO cp -R "$app_dir/Contents/Resources/"* "$install_dir/"
|
|
|
|
# Create wrapper script so satdump can find its resources via @executable_path
|
|
$SUDO tee /usr/local/bin/satdump >/dev/null <<'WRAPPER'
|
|
#!/bin/sh
|
|
exec /usr/local/lib/satdump/satdump "$@"
|
|
WRAPPER
|
|
$SUDO chmod +x /usr/local/bin/satdump
|
|
|
|
hdiutil detach "$tmp_dir/mnt" -quiet 2>/dev/null
|
|
|
|
# Verify installation
|
|
if /usr/local/lib/satdump/satdump 2>&1 | grep -q "Usage"; then
|
|
ok "SatDump v1.2.2 installed successfully."
|
|
else
|
|
warn "SatDump installed but may not work correctly."
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_macos_packages() {
|
|
need_sudo
|
|
|
|
# Prime sudo credentials upfront so builds don't prompt mid-compilation
|
|
if [[ -n "${SUDO:-}" ]]; then
|
|
info "Some tools require sudo to install. You may be prompted for your password."
|
|
sudo -v || { fail "sudo authentication failed"; exit 1; }
|
|
fi
|
|
|
|
TOTAL_STEPS=22
|
|
CURRENT_STEP=0
|
|
|
|
progress "Checking Homebrew"
|
|
ensure_brew
|
|
|
|
progress "Installing RTL-SDR libraries"
|
|
brew_install librtlsdr
|
|
|
|
progress "Installing multimon-ng"
|
|
# multimon-ng is not in Homebrew core, so build from source
|
|
if ! cmd_exists multimon-ng; then
|
|
install_multimon_ng_from_source_macos
|
|
else
|
|
ok "multimon-ng already installed"
|
|
fi
|
|
|
|
progress "Installing direwolf (APRS decoder)"
|
|
(brew_install direwolf) || warn "direwolf not available via Homebrew"
|
|
|
|
progress "SSTV decoder"
|
|
ok "SSTV uses built-in pure Python decoder (no external tools needed)"
|
|
|
|
progress "Installing DSD (Digital Speech Decoder, optional)"
|
|
if ! cmd_exists dsd && ! cmd_exists dsd-fme; then
|
|
echo
|
|
info "DSD is used for DMR, P25, NXDN, and D-STAR digital voice decoding."
|
|
if ask_yes_no "Do you want to install DSD?"; then
|
|
install_dsd_from_source || warn "DSD build failed. DMR/P25 decoding will not be available."
|
|
else
|
|
warn "Skipping DSD installation. DMR/P25 decoding will not be available."
|
|
fi
|
|
else
|
|
ok "DSD already installed"
|
|
fi
|
|
|
|
progress "Installing ffmpeg"
|
|
brew_install ffmpeg
|
|
|
|
progress "Installing rtl_433"
|
|
brew_install rtl_433
|
|
|
|
progress "Installing HackRF tools"
|
|
brew_install hackrf
|
|
|
|
progress "Installing rtlamr (optional)"
|
|
# rtlamr is optional - used for utility meter monitoring
|
|
if ! cmd_exists rtlamr; then
|
|
echo
|
|
info "rtlamr is used for utility meter monitoring (electric/gas/water meters)."
|
|
if ask_yes_no "Do you want to install rtlamr?"; then
|
|
install_rtlamr_from_source
|
|
else
|
|
warn "Skipping rtlamr installation. You can install it later if needed."
|
|
fi
|
|
else
|
|
ok "rtlamr already installed"
|
|
fi
|
|
|
|
progress "Installing dump1090"
|
|
if ! cmd_exists dump1090; then
|
|
(brew_install dump1090-mutability) || install_dump1090_from_source_macos || warn "dump1090 not available"
|
|
else
|
|
ok "dump1090 already installed"
|
|
fi
|
|
|
|
progress "Installing acarsdec"
|
|
if ! cmd_exists acarsdec; then
|
|
(brew_install acarsdec) || install_acarsdec_from_source_macos || warn "acarsdec not available"
|
|
else
|
|
ok "acarsdec already installed"
|
|
fi
|
|
|
|
progress "Installing dumpvdl2"
|
|
if ! cmd_exists dumpvdl2; then
|
|
install_dumpvdl2_from_source_macos || warn "dumpvdl2 not available. VDL2 decoding will not be available."
|
|
else
|
|
ok "dumpvdl2 already installed"
|
|
fi
|
|
|
|
progress "Installing AIS-catcher"
|
|
if ! cmd_exists AIS-catcher && ! cmd_exists aiscatcher; then
|
|
(brew_install aiscatcher) || install_aiscatcher_from_source_macos || warn "AIS-catcher not available"
|
|
else
|
|
ok "AIS-catcher already installed"
|
|
fi
|
|
|
|
progress "Installing SatDump (optional)"
|
|
if ! cmd_exists satdump; then
|
|
echo
|
|
info "SatDump is used for weather satellite imagery (NOAA APT & Meteor LRPT)."
|
|
if ask_yes_no "Do you want to install SatDump?"; then
|
|
install_satdump_macos || warn "SatDump installation failed. Weather satellite decoding will not be available."
|
|
else
|
|
warn "Skipping SatDump installation. You can install it later if needed."
|
|
fi
|
|
else
|
|
ok "SatDump already installed"
|
|
fi
|
|
|
|
progress "Installing aircrack-ng"
|
|
brew_install aircrack-ng
|
|
|
|
progress "Installing hcxtools"
|
|
brew_install hcxtools
|
|
|
|
progress "Installing SoapySDR"
|
|
brew_install soapysdr
|
|
|
|
progress "Installing gpsd"
|
|
brew_install gpsd
|
|
|
|
progress "Installing Ubertooth tools (optional)"
|
|
if ! cmd_exists ubertooth-btle; then
|
|
echo
|
|
info "Ubertooth is used for advanced Bluetooth packet sniffing with Ubertooth One hardware."
|
|
if ask_yes_no "Do you want to install Ubertooth tools?"; then
|
|
brew_install ubertooth || warn "Ubertooth not available via Homebrew"
|
|
else
|
|
warn "Skipping Ubertooth installation. You can install it later if needed."
|
|
fi
|
|
else
|
|
ok "Ubertooth already installed"
|
|
fi
|
|
|
|
warn "macOS note: hcitool/hciconfig are Linux (BlueZ) utilities and often unavailable on macOS."
|
|
info "TSCM BLE scanning uses bleak library (installed via pip) for manufacturer data detection."
|
|
echo
|
|
}
|
|
|
|
# ----------------------------
|
|
# Debian/Ubuntu install (APT)
|
|
# ----------------------------
|
|
apt_install() {
|
|
local pkgs="$*"
|
|
local output
|
|
local ret=0
|
|
output=$($SUDO apt-get install -y --no-install-recommends "$@" 2>&1) || ret=$?
|
|
if [[ $ret -ne 0 ]]; then
|
|
fail "Failed to install: $pkgs"
|
|
echo "$output" | tail -10
|
|
fail "Try running: sudo apt-get update && sudo apt-get install -y $pkgs"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
apt_try_install_any() {
|
|
local p
|
|
for p in "$@"; do
|
|
if $SUDO apt-get install -y --no-install-recommends "$p" >/dev/null 2>&1; then
|
|
ok "apt: installed ${p}"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
apt_install_if_missing() {
|
|
local pkg="$1"
|
|
if dpkg -l "$pkg" 2>/dev/null | grep -q "^ii"; then
|
|
ok "apt: ${pkg} already installed"
|
|
return 0
|
|
fi
|
|
apt_install "$pkg"
|
|
}
|
|
|
|
install_dump1090_from_source_debian() {
|
|
info "dump1090 not available via APT. Building from source (required)..."
|
|
|
|
apt_install build-essential git pkg-config \
|
|
librtlsdr-dev libusb-1.0-0-dev \
|
|
libncurses-dev tcl-dev python3-dev
|
|
|
|
local JOBS
|
|
JOBS="$(nproc 2>/dev/null || echo 1)"
|
|
|
|
# Run in subshell to isolate EXIT trap
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'kill "$progress_pid" 2>/dev/null; wait "$progress_pid" 2>/dev/null; rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning FlightAware dump1090..."
|
|
git clone --depth 1 https://github.com/flightaware/dump1090.git "$tmp_dir/dump1090" >/dev/null 2>&1 \
|
|
|| { fail "Failed to clone FlightAware dump1090"; exit 1; }
|
|
|
|
cd "$tmp_dir/dump1090"
|
|
# Remove -Werror to prevent build failures on newer GCC versions
|
|
sed -i 's/-Werror//g' Makefile 2>/dev/null || true
|
|
info "Compiling FlightAware dump1090 (using ${JOBS} CPU cores)..."
|
|
build_log="$tmp_dir/dump1090-build.log"
|
|
|
|
(while true; do sleep 20; printf " [*] Still compiling dump1090...\n"; done) &
|
|
progress_pid=$!
|
|
|
|
if make -j "$JOBS" BLADERF=no RTLSDR=yes >"$build_log" 2>&1; then
|
|
kill "$progress_pid" 2>/dev/null; wait "$progress_pid" 2>/dev/null; progress_pid=
|
|
$SUDO install -m 0755 dump1090 /usr/local/bin/dump1090
|
|
ok "dump1090 installed successfully (FlightAware)."
|
|
exit 0
|
|
fi
|
|
|
|
kill "$progress_pid" 2>/dev/null; wait "$progress_pid" 2>/dev/null; progress_pid=
|
|
warn "FlightAware build failed. Falling back to wiedehopf/readsb..."
|
|
warn "Build log (last 20 lines):"
|
|
tail -20 "$build_log" | while IFS= read -r line; do warn " $line"; done
|
|
|
|
rm -rf "$tmp_dir/dump1090"
|
|
git clone --depth 1 https://github.com/wiedehopf/readsb.git "$tmp_dir/dump1090" >/dev/null 2>&1 \
|
|
|| { fail "Failed to clone wiedehopf/readsb"; exit 1; }
|
|
|
|
cd "$tmp_dir/dump1090"
|
|
info "Compiling readsb (using ${JOBS} CPU cores)..."
|
|
build_log="$tmp_dir/readsb-build.log"
|
|
|
|
(while true; do sleep 20; printf " [*] Still compiling readsb...\n"; done) &
|
|
progress_pid=$!
|
|
|
|
if ! make -j "$JOBS" RTLSDR=yes >"$build_log" 2>&1; then
|
|
kill "$progress_pid" 2>/dev/null; wait "$progress_pid" 2>/dev/null; progress_pid=
|
|
warn "Build log (last 20 lines):"
|
|
tail -20 "$build_log" | while IFS= read -r line; do warn " $line"; done
|
|
fail "Failed to build readsb from source (required)."
|
|
exit 1
|
|
fi
|
|
|
|
kill "$progress_pid" 2>/dev/null; wait "$progress_pid" 2>/dev/null; progress_pid=
|
|
$SUDO install -m 0755 readsb /usr/local/bin/dump1090
|
|
ok "dump1090 installed successfully (via readsb)."
|
|
)
|
|
}
|
|
|
|
install_acarsdec_from_source_debian() {
|
|
info "acarsdec not available via APT. Building from source..."
|
|
|
|
apt_install build-essential git cmake \
|
|
librtlsdr-dev libusb-1.0-0-dev libsndfile1-dev
|
|
|
|
# Run in subshell to isolate EXIT trap
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning acarsdec..."
|
|
git clone --depth 1 https://github.com/TLeconte/acarsdec.git "$tmp_dir/acarsdec" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone acarsdec"; exit 1; }
|
|
|
|
cd "$tmp_dir/acarsdec"
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling acarsdec..."
|
|
if cmake .. -Drtl=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5 >/dev/null 2>&1 && make >/dev/null 2>&1; then
|
|
$SUDO install -m 0755 acarsdec /usr/local/bin/acarsdec
|
|
ok "acarsdec installed successfully."
|
|
else
|
|
warn "Failed to build acarsdec from source. ACARS decoding will not be available."
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_dumpvdl2_from_source_debian() {
|
|
info "Building dumpvdl2 from source (with libacars dependency)..."
|
|
|
|
apt_install build-essential git cmake \
|
|
librtlsdr-dev libusb-1.0-0-dev libglib2.0-dev libxml2-dev
|
|
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
# Build libacars first
|
|
info "Cloning libacars..."
|
|
git clone --depth 1 https://github.com/szpajder/libacars.git "$tmp_dir/libacars" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone libacars"; exit 1; }
|
|
|
|
cd "$tmp_dir/libacars"
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling libacars..."
|
|
if cmake .. >/dev/null 2>&1 && make >/dev/null 2>&1; then
|
|
$SUDO make install >/dev/null 2>&1
|
|
$SUDO ldconfig
|
|
ok "libacars installed"
|
|
else
|
|
warn "Failed to build libacars."
|
|
exit 1
|
|
fi
|
|
|
|
# Build dumpvdl2
|
|
info "Cloning dumpvdl2..."
|
|
git clone --depth 1 https://github.com/szpajder/dumpvdl2.git "$tmp_dir/dumpvdl2" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone dumpvdl2"; exit 1; }
|
|
|
|
cd "$tmp_dir/dumpvdl2"
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling dumpvdl2..."
|
|
if cmake .. >/dev/null 2>&1 && make >/dev/null 2>&1; then
|
|
$SUDO install -m 0755 src/dumpvdl2 /usr/local/bin/dumpvdl2
|
|
ok "dumpvdl2 installed successfully."
|
|
else
|
|
warn "Failed to build dumpvdl2 from source. VDL2 decoding will not be available."
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_aiscatcher_from_source_debian() {
|
|
info "AIS-catcher not available via APT. Building from source..."
|
|
|
|
apt_install build-essential git cmake pkg-config \
|
|
librtlsdr-dev libusb-1.0-0-dev libcurl4-openssl-dev zlib1g-dev
|
|
|
|
# Run in subshell to isolate EXIT trap
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning AIS-catcher..."
|
|
git clone --depth 1 https://github.com/jvde-github/AIS-catcher.git "$tmp_dir/AIS-catcher" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone AIS-catcher"; exit 1; }
|
|
|
|
cd "$tmp_dir/AIS-catcher"
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling AIS-catcher..."
|
|
if cmake .. >/dev/null 2>&1 && make >/dev/null 2>&1; then
|
|
$SUDO install -m 0755 AIS-catcher /usr/local/bin/AIS-catcher
|
|
ok "AIS-catcher installed successfully."
|
|
else
|
|
warn "Failed to build AIS-catcher from source. AIS vessel tracking will not be available."
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_ubertooth_from_source_debian() {
|
|
info "Building Ubertooth from source..."
|
|
|
|
apt_install build-essential git cmake libusb-1.0-0-dev pkg-config libbluetooth-dev
|
|
|
|
# Run in subshell to isolate EXIT trap
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning Ubertooth..."
|
|
git clone --depth 1 https://github.com/greatscottgadgets/ubertooth.git "$tmp_dir/ubertooth" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone Ubertooth"; exit 1; }
|
|
|
|
cd "$tmp_dir/ubertooth/host"
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling Ubertooth..."
|
|
if cmake .. >/dev/null 2>&1 && make >/dev/null 2>&1; then
|
|
$SUDO make install >/dev/null 2>&1
|
|
$SUDO ldconfig
|
|
ok "Ubertooth installed successfully from source."
|
|
else
|
|
warn "Failed to build Ubertooth from source."
|
|
fi
|
|
)
|
|
}
|
|
|
|
install_rtlsdr_blog_drivers_debian() {
|
|
# The RTL-SDR Blog drivers provide better support for:
|
|
# - RTL-SDR Blog V4 (R828D tuner)
|
|
# - RTL-SDR Blog V3 with bias-t improvements
|
|
# - Better overall compatibility with all RTL-SDR devices
|
|
# These drivers are backward compatible with standard RTL-SDR devices.
|
|
|
|
info "Installing RTL-SDR Blog drivers (improved V4 support)..."
|
|
|
|
# Install build dependencies
|
|
apt_install build-essential git cmake libusb-1.0-0-dev pkg-config
|
|
|
|
# Run in subshell to isolate EXIT trap
|
|
(
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp_dir"' EXIT
|
|
|
|
info "Cloning RTL-SDR Blog driver fork..."
|
|
git clone https://github.com/rtlsdrblog/rtl-sdr-blog.git "$tmp_dir/rtl-sdr-blog" >/dev/null 2>&1 \
|
|
|| { warn "Failed to clone RTL-SDR Blog drivers"; exit 1; }
|
|
|
|
cd "$tmp_dir/rtl-sdr-blog"
|
|
mkdir -p build && cd build
|
|
|
|
info "Compiling RTL-SDR Blog drivers..."
|
|
if cmake .. -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON >/dev/null 2>&1 && make >/dev/null 2>&1; then
|
|
$SUDO make install >/dev/null 2>&1
|
|
$SUDO ldconfig
|
|
|
|
# Copy udev rules if they exist
|
|
if [[ -f ../rtl-sdr.rules ]]; then
|
|
$SUDO cp ../rtl-sdr.rules /etc/udev/rules.d/20-rtlsdr-blog.rules
|
|
$SUDO udevadm control --reload-rules || true
|
|
$SUDO udevadm trigger || true
|
|
fi
|
|
|
|
ok "RTL-SDR Blog drivers installed successfully."
|
|
info "These drivers provide improved support for RTL-SDR Blog V4 and other devices."
|
|
warn "Unplug and replug your RTL-SDR devices for the new drivers to take effect."
|
|
else
|
|
warn "Failed to build RTL-SDR Blog drivers. Using stock drivers."
|
|
warn "If you have an RTL-SDR Blog V4, you may need to install drivers manually."
|
|
warn "See: https://github.com/rtlsdrblog/rtl-sdr-blog"
|
|
fi
|
|
)
|
|
}
|
|
|
|
setup_udev_rules_debian() {
|
|
[[ -d /etc/udev/rules.d ]] || { warn "udev not found; skipping RTL-SDR udev rules."; return 0; }
|
|
|
|
local rules_file="/etc/udev/rules.d/20-rtlsdr.rules"
|
|
[[ -f "$rules_file" ]] && { ok "RTL-SDR udev rules already present: $rules_file"; return 0; }
|
|
|
|
info "Installing RTL-SDR udev rules..."
|
|
$SUDO tee "$rules_file" >/dev/null <<'EOF'
|
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", MODE="0666"
|
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2832", MODE="0666"
|
|
EOF
|
|
$SUDO udevadm control --reload-rules || true
|
|
$SUDO udevadm trigger || true
|
|
ok "udev rules installed. Unplug/replug your RTL-SDR if connected."
|
|
echo
|
|
}
|
|
|
|
blacklist_kernel_drivers_debian() {
|
|
local blacklist_file="/etc/modprobe.d/blacklist-rtlsdr.conf"
|
|
|
|
if [[ -f "$blacklist_file" ]]; then
|
|
ok "RTL-SDR kernel driver blacklist already present"
|
|
return 0
|
|
fi
|
|
|
|
info "Blacklisting conflicting DVB kernel drivers..."
|
|
$SUDO tee "$blacklist_file" >/dev/null <<'EOF'
|
|
# Blacklist DVB-T drivers to allow rtl-sdr to access RTL2832U devices
|
|
blacklist dvb_usb_rtl28xxu
|
|
blacklist rtl2832
|
|
blacklist rtl2830
|
|
blacklist r820t
|
|
EOF
|
|
|
|
# Unload modules if currently loaded
|
|
for mod in dvb_usb_rtl28xxu rtl2832 rtl2830 r820t; do
|
|
if lsmod | grep -q "^$mod"; then
|
|
$SUDO modprobe -r "$mod" 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
ok "Kernel drivers blacklisted. Unplug/replug your RTL-SDR if connected."
|
|
echo
|
|
}
|
|
|
|
install_debian_packages() {
|
|
need_sudo
|
|
|
|
# Keep APT interactive when a TTY is available.
|
|
if $NON_INTERACTIVE; then
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
export NEEDRESTART_MODE=a
|
|
elif [[ -t 0 ]]; then
|
|
export DEBIAN_FRONTEND=readline
|
|
export NEEDRESTART_MODE=a
|
|
else
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
export NEEDRESTART_MODE=a
|
|
fi
|
|
|
|
TOTAL_STEPS=28
|
|
CURRENT_STEP=0
|
|
|
|
progress "Updating APT package lists"
|
|
$SUDO apt-get update -y >/dev/null
|
|
|
|
progress "Installing RTL-SDR"
|
|
if ! $IS_DRAGONOS; then
|
|
# Handle package conflict between librtlsdr0 and librtlsdr2
|
|
# The newer librtlsdr0 (2.0.2) conflicts with older librtlsdr2 (2.0.1)
|
|
if dpkg -l | grep -q "librtlsdr2"; then
|
|
info "Detected librtlsdr2 conflict - upgrading to librtlsdr0..."
|
|
|
|
# Remove packages that depend on librtlsdr2, then remove librtlsdr2
|
|
# These will be reinstalled with librtlsdr0 support
|
|
$SUDO apt-get remove -y dump1090-mutability libgnuradio-osmosdr0.2.0t64 rtl-433 librtlsdr2 rtl-sdr 2>/dev/null || true
|
|
$SUDO apt-get autoremove -y 2>/dev/null || true
|
|
|
|
ok "Removed conflicting librtlsdr2 packages"
|
|
fi
|
|
|
|
# If rtl-sdr is in broken state, remove it completely first
|
|
if dpkg -l | grep -q "^.[^i].*rtl-sdr" || ! dpkg -l rtl-sdr 2>/dev/null | grep -q "^ii"; then
|
|
info "Removing broken rtl-sdr package..."
|
|
$SUDO dpkg --remove --force-remove-reinstreq rtl-sdr 2>/dev/null || true
|
|
$SUDO dpkg --purge --force-remove-reinstreq rtl-sdr 2>/dev/null || true
|
|
fi
|
|
|
|
# Force remove librtlsdr2 if it still exists
|
|
if dpkg -l | grep -q "librtlsdr2"; then
|
|
info "Force removing librtlsdr2..."
|
|
$SUDO dpkg --remove --force-all librtlsdr2 2>/dev/null || true
|
|
$SUDO dpkg --purge --force-all librtlsdr2 2>/dev/null || true
|
|
fi
|
|
|
|
# Clean up any partial installations
|
|
$SUDO dpkg --configure -a 2>/dev/null || true
|
|
$SUDO apt-get --fix-broken install -y 2>/dev/null || true
|
|
fi
|
|
|
|
apt_install_if_missing rtl-sdr
|
|
|
|
progress "RTL-SDR Blog drivers"
|
|
if cmd_exists rtl_test; then
|
|
ok "RTL-SDR drivers already installed"
|
|
else
|
|
info "RTL-SDR drivers not found, installing RTL-SDR Blog drivers..."
|
|
install_rtlsdr_blog_drivers_debian
|
|
fi
|
|
|
|
progress "Installing multimon-ng"
|
|
apt_install multimon-ng
|
|
|
|
progress "Installing direwolf (APRS decoder)"
|
|
apt_install direwolf || true
|
|
|
|
progress "SSTV decoder"
|
|
ok "SSTV uses built-in pure Python decoder (no external tools needed)"
|
|
|
|
progress "Installing DSD (Digital Speech Decoder, optional)"
|
|
if ! cmd_exists dsd && ! cmd_exists dsd-fme; then
|
|
echo
|
|
info "DSD is used for DMR, P25, NXDN, and D-STAR digital voice decoding."
|
|
if ask_yes_no "Do you want to install DSD?"; then
|
|
install_dsd_from_source || warn "DSD build failed. DMR/P25 decoding will not be available."
|
|
else
|
|
warn "Skipping DSD installation. DMR/P25 decoding will not be available."
|
|
fi
|
|
else
|
|
ok "DSD already installed"
|
|
fi
|
|
|
|
progress "Installing ffmpeg"
|
|
apt_install ffmpeg
|
|
|
|
progress "Installing rtl_433"
|
|
apt_try_install_any rtl-433 rtl433 || warn "rtl-433 not available"
|
|
|
|
progress "Installing HackRF tools"
|
|
apt_install hackrf || warn "hackrf tools not available"
|
|
|
|
progress "Installing rtlamr (optional)"
|
|
# rtlamr is optional - used for utility meter monitoring
|
|
if ! cmd_exists rtlamr; then
|
|
echo
|
|
info "rtlamr is used for utility meter monitoring (electric/gas/water meters)."
|
|
if ask_yes_no "Do you want to install rtlamr?"; then
|
|
install_rtlamr_from_source
|
|
else
|
|
warn "Skipping rtlamr installation. You can install it later if needed."
|
|
fi
|
|
else
|
|
ok "rtlamr already installed"
|
|
fi
|
|
|
|
progress "Installing aircrack-ng"
|
|
apt_install aircrack-ng || true
|
|
|
|
progress "Installing hcxdumptool"
|
|
apt_install hcxdumptool || true
|
|
|
|
progress "Installing hcxtools"
|
|
apt_install hcxtools || true
|
|
|
|
progress "Installing Bluetooth tools"
|
|
apt_install bluez bluetooth || true
|
|
|
|
progress "Installing Ubertooth tools (optional)"
|
|
if ! cmd_exists ubertooth-btle; then
|
|
echo
|
|
info "Ubertooth is used for advanced Bluetooth packet sniffing with Ubertooth One hardware."
|
|
if ask_yes_no "Do you want to install Ubertooth tools?"; then
|
|
apt_install libubertooth-dev ubertooth || install_ubertooth_from_source_debian
|
|
else
|
|
warn "Skipping Ubertooth installation. You can install it later if needed."
|
|
fi
|
|
else
|
|
ok "Ubertooth already installed"
|
|
fi
|
|
|
|
progress "Installing SoapySDR"
|
|
# Exclude xtrx-dkms - its kernel module fails to build on newer kernels (6.14+)
|
|
# and causes apt to hang. Most users don't have XTRX hardware anyway.
|
|
apt_install soapysdr-tools xtrx-dkms- || true
|
|
|
|
progress "Installing gpsd"
|
|
apt_install gpsd gpsd-clients || true
|
|
|
|
progress "Installing Python packages"
|
|
apt_install python3-venv python3-pip || true
|
|
# Install Python packages via apt (more reliable than pip on modern Debian/Ubuntu)
|
|
$SUDO apt-get install -y python3-flask python3-requests python3-serial >/dev/null 2>&1 || true
|
|
$SUDO apt-get install -y python3-skyfield >/dev/null 2>&1 || true
|
|
# bleak for BLE scanning with manufacturer data (TSCM mode)
|
|
$SUDO apt-get install -y python3-bleak >/dev/null 2>&1 || true
|
|
|
|
progress "Installing dump1090"
|
|
if ! cmd_exists dump1090 && ! cmd_exists dump1090-mutability; then
|
|
apt_try_install_any dump1090-fa dump1090-mutability dump1090 || true
|
|
fi
|
|
if ! cmd_exists dump1090; then
|
|
if cmd_exists dump1090-mutability; then
|
|
$SUDO ln -s "$(which dump1090-mutability)" /usr/local/sbin/dump1090
|
|
fi
|
|
fi
|
|
cmd_exists dump1090 || install_dump1090_from_source_debian
|
|
|
|
progress "Installing acarsdec"
|
|
if ! cmd_exists acarsdec; then
|
|
apt_install acarsdec || true
|
|
fi
|
|
cmd_exists acarsdec || install_acarsdec_from_source_debian
|
|
|
|
progress "Installing dumpvdl2"
|
|
if ! cmd_exists dumpvdl2; then
|
|
install_dumpvdl2_from_source_debian || warn "dumpvdl2 not available. VDL2 decoding will not be available."
|
|
else
|
|
ok "dumpvdl2 already installed"
|
|
fi
|
|
|
|
progress "Installing AIS-catcher"
|
|
if ! cmd_exists AIS-catcher && ! cmd_exists aiscatcher; then
|
|
install_aiscatcher_from_source_debian
|
|
else
|
|
ok "AIS-catcher already installed"
|
|
fi
|
|
|
|
progress "Installing SatDump (optional)"
|
|
if ! cmd_exists satdump; then
|
|
echo
|
|
info "SatDump is used for weather satellite imagery (NOAA APT & Meteor LRPT)."
|
|
if ask_yes_no "Do you want to install SatDump?"; then
|
|
install_satdump_from_source_debian || warn "SatDump build failed. Weather satellite decoding will not be available."
|
|
else
|
|
warn "Skipping SatDump installation. You can install it later if needed."
|
|
fi
|
|
else
|
|
ok "SatDump already installed"
|
|
fi
|
|
|
|
progress "Configuring udev rules"
|
|
setup_udev_rules_debian
|
|
|
|
progress "Kernel driver configuration"
|
|
if $IS_DRAGONOS; then
|
|
info "DragonOS already has RTL-SDR drivers configured correctly."
|
|
elif [[ -f /etc/modprobe.d/blacklist-rtlsdr.conf ]]; then
|
|
ok "DVB kernel drivers already blacklisted"
|
|
else
|
|
echo
|
|
echo "The DVB-T kernel drivers conflict with RTL-SDR userspace access."
|
|
echo "Blacklisting them allows rtl_sdr tools to access the device."
|
|
if ask_yes_no "Blacklist conflicting kernel drivers?"; then
|
|
blacklist_kernel_drivers_debian
|
|
else
|
|
warn "Skipped kernel driver blacklist. RTL-SDR may not work without manual config."
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ----------------------------
|
|
# Final summary / hard fail
|
|
# ----------------------------
|
|
final_summary_and_hard_fail() {
|
|
check_tools
|
|
|
|
echo "============================================"
|
|
echo
|
|
echo "To start INTERCEPT:"
|
|
echo " sudo -E venv/bin/python intercept.py"
|
|
echo
|
|
echo "Then open http://localhost:5050 in your browser"
|
|
echo
|
|
echo "============================================"
|
|
|
|
if [[ "${#missing_required[@]}" -eq 0 ]]; then
|
|
ok "All REQUIRED tools are installed."
|
|
else
|
|
fail "Missing REQUIRED tools:"
|
|
for t in "${missing_required[@]}"; do echo " - $t"; done
|
|
echo
|
|
if [[ "$OS" == "macos" ]]; then
|
|
warn "macOS note: bluetoothctl/hcitool/hciconfig are Linux (BlueZ) tools and unavailable on macOS."
|
|
warn "Bluetooth functionality will be limited. Other features should work."
|
|
else
|
|
fail "Exiting because required tools are missing."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [[ "${#missing_recommended[@]}" -gt 0 ]]; then
|
|
echo
|
|
warn "Missing RECOMMENDED tools (some features will not work):"
|
|
for t in "${missing_recommended[@]}"; do echo " - $t"; done
|
|
echo
|
|
warn "Install these for full functionality"
|
|
fi
|
|
}
|
|
|
|
# ----------------------------
|
|
# Pre-flight summary
|
|
# ----------------------------
|
|
show_install_summary() {
|
|
info "Installation Summary:"
|
|
echo
|
|
echo " OS: $OS"
|
|
$IS_DRAGONOS && echo " DragonOS: Yes (safe mode enabled)"
|
|
echo
|
|
echo " This script will:"
|
|
echo " - Install missing SDR tools (rtl-sdr, multimon-ng, etc.)"
|
|
echo " - Install Python dependencies in a virtual environment"
|
|
echo
|
|
if ! $IS_DRAGONOS; then
|
|
echo " You will be prompted before:"
|
|
echo " - Installing RTL-SDR Blog drivers (replaces existing)"
|
|
echo " - Blacklisting kernel DVB drivers"
|
|
fi
|
|
echo
|
|
if $NON_INTERACTIVE; then
|
|
info "Non-interactive mode: continuing without prompt."
|
|
return
|
|
fi
|
|
if ! ask_yes_no "Continue with installation?" "y"; then
|
|
info "Installation cancelled."
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
# ----------------------------
|
|
# MAIN
|
|
# ----------------------------
|
|
main() {
|
|
detect_os
|
|
detect_dragonos
|
|
show_install_summary
|
|
|
|
if [[ "$OS" == "macos" ]]; then
|
|
install_macos_packages
|
|
else
|
|
install_debian_packages
|
|
fi
|
|
|
|
install_python_deps
|
|
|
|
# Download leaflet-heat plugin (offline mode)
|
|
if [ ! -f "static/vendor/leaflet-heat/leaflet-heat.js" ]; then
|
|
info "Downloading leaflet-heat plugin..."
|
|
mkdir -p static/vendor/leaflet-heat
|
|
if curl -sL "https://unpkg.com/leaflet.heat@0.2.0/dist/leaflet-heat.js" \
|
|
-o static/vendor/leaflet-heat/leaflet-heat.js; then
|
|
ok "leaflet-heat plugin downloaded"
|
|
else
|
|
warn "Failed to download leaflet-heat plugin. Heatmap will use CDN."
|
|
fi
|
|
fi
|
|
|
|
final_summary_and_hard_fail
|
|
}
|
|
|
|
main "$@"
|
|
|
|
# Clear traps before exiting to prevent spurious errors during cleanup
|
|
trap - ERR EXIT
|
|
exit 0
|