mirror of
https://github.com/smittix/intercept.git
synced 2026-04-25 07:10:00 -07:00
If dump1090-mutability was installed by a previous run and later removed (e.g. by apt removing it as a reverse dep), the symlink at /usr/local/sbin/dump1090 is left pointing at a non-existent target. cmd_exists finds the broken symlink and treats dump1090 as installed, so the real install is skipped and running dump1090 gives "No such file or directory". Before the install check, resolve the command path and delete it if it exists in PATH but is not executable (broken symlink). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1731 lines
56 KiB
Bash
Executable File
1731 lines
56 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 --quiet "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>/dev/null || 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)..."
|
|
|
|
# Core deps — hard-fail if missing
|
|
apt_install build-essential git cmake pkg-config \
|
|
libpng-dev libtiff-dev libzstd-dev \
|
|
libsqlite3-dev libcurl4-openssl-dev zlib1g-dev libzmq3-dev libfftw3-dev
|
|
|
|
# libvolk: package name differs between distros
|
|
# Ubuntu / Debian Trixie+: libvolk-dev
|
|
# Raspberry Pi OS Bookworm / Debian Bookworm: libvolk2-dev
|
|
apt_try_install_any libvolk-dev libvolk2-dev \
|
|
|| warn "libvolk not found — SatDump will build without VOLK acceleration"
|
|
|
|
# Optional SDR hardware libs — soft-fail so missing hardware doesn't abort
|
|
for pkg in libjemalloc-dev libnng-dev libsoapysdr-dev libhackrf-dev liblimesuite-dev; do
|
|
$SUDO apt-get install -y --no-install-recommends "$pkg" >/dev/null 2>&1 \
|
|
|| warn "${pkg} not available — skipping (SatDump can build without it)"
|
|
done
|
|
|
|
# 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 # ensure the file ends with a newline before the closing pragma
|
|
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 '{ [[ -n "${progress_pid:-}" ]] && kill "$progress_pid" 2>/dev/null && wait "$progress_pid" 2>/dev/null || true; }; 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 || true; 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 || true; 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 || true; 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 || true; 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
|
|
|
|
# Make the Blog drivers' library take priority over the apt-installed
|
|
# librtlsdr. Removing apt packages is too destructive (dump1090-mutability
|
|
# and other tools depend on librtlsdr0 and get swept out). Instead,
|
|
# prepend /usr/local/lib to ldconfig's search path — files named 00-*
|
|
# sort before the distro's aarch64-linux-gnu.conf — so ldconfig lists
|
|
# /usr/local/lib/librtlsdr.so.0 first and the dynamic linker uses it.
|
|
if [[ -d /etc/ld.so.conf.d ]]; then
|
|
echo '/usr/local/lib' | $SUDO tee /etc/ld.so.conf.d/00-local-first.conf >/dev/null
|
|
fi
|
|
$SUDO ldconfig
|
|
|
|
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"
|
|
else
|
|
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
|
|
fi
|
|
|
|
# Always unload modules if currently loaded — this must happen even on
|
|
# re-runs where the blacklist file already exists, since the modules may
|
|
# still be live from the current boot (e.g. blacklist wasn't in initramfs).
|
|
local unloaded=false
|
|
for mod in dvb_usb_rtl28xxu rtl2832 rtl2830 r820t; do
|
|
if lsmod | grep -q "^$mod"; then
|
|
$SUDO modprobe -r "$mod" 2>/dev/null || true
|
|
unloaded=true
|
|
fi
|
|
done
|
|
$unloaded && info "Unloaded conflicting DVB kernel modules from current session."
|
|
|
|
# Bake the blacklist into the initramfs so it survives reboots on
|
|
# Raspberry Pi OS / Debian (without this the modules can reload on boot).
|
|
if cmd_exists update-initramfs; then
|
|
info "Updating initramfs to persist driver blacklist across reboots..."
|
|
$SUDO update-initramfs -u >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
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 (V4 support)"
|
|
if $IS_DRAGONOS; then
|
|
info "DragonOS: skipping RTL-SDR Blog driver install (pre-configured)."
|
|
else
|
|
echo
|
|
info "RTL-SDR Blog drivers add V4 (R828D tuner) support and bias-tee improvements."
|
|
info "They are backward-compatible with all RTL-SDR devices."
|
|
if ask_yes_no "Install RTL-SDR Blog drivers? (recommended for V4 users, safe for all)" "y"; then
|
|
install_rtlsdr_blog_drivers_debian
|
|
else
|
|
warn "Skipping RTL-SDR Blog drivers. V4 devices may not work correctly."
|
|
fi
|
|
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"
|
|
# Remove any stale symlink left from a previous run where dump1090-mutability
|
|
# was later uninstalled — cmd_exists finds the broken symlink and skips the
|
|
# real install, leaving dump1090 seemingly present but non-functional.
|
|
local dump1090_path
|
|
dump1090_path="$(command -v dump1090 2>/dev/null || true)"
|
|
if [[ -n "$dump1090_path" ]] && [[ ! -x "$dump1090_path" ]]; then
|
|
info "Removing broken dump1090 symlink: $dump1090_path"
|
|
$SUDO rm -f "$dump1090_path"
|
|
fi
|
|
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
|