#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="${SCRIPT_DIR}" VENV_PYTHON="${PROJECT_ROOT}/.venv/bin/python" VENV_PIP="${PROJECT_ROOT}/.venv/bin/pip" GUI_ENTRY="${PROJECT_ROOT}/python_app/gui/main.py" REQUIREMENTS_FILE="${PROJECT_ROOT}/requirements.txt" SKIP_BUILD=0 BUILD_ONLY=0 CLEAN_SHM=0 print_usage() { cat <<'EOF' Usage: ./start.sh [options] Options: --skip-build Skip C++ build step and only run GUI --build-only Build C++ binaries and exit --clean-shm Remove known radar shared-memory segments before start -h, --help Show this help EOF } parse_args() { while (($# > 0)); do case "$1" in --skip-build) SKIP_BUILD=1 ;; --build-only) BUILD_ONLY=1 ;; --clean-shm) CLEAN_SHM=1 ;; -h|--help) print_usage exit 0 ;; *) echo "Unknown argument: $1" >&2 print_usage exit 1 ;; esac shift done } check_environment() { if ! command -v python3 >/dev/null 2>&1; then echo "python3 is not installed or not found in PATH." >&2 exit 1 fi if [[ ! -f "${REQUIREMENTS_FILE}" ]]; then echo "Requirements file not found: ${REQUIREMENTS_FILE}" >&2 exit 1 fi if [[ ! -f "${GUI_ENTRY}" ]]; then echo "GUI entry not found: ${GUI_ENTRY}" >&2 exit 1 fi } ensure_python_dependencies() { if [[ ! -x "${VENV_PYTHON}" ]]; then echo "[start.sh] Creating virtual environment..." python3 -m venv "${PROJECT_ROOT}/.venv" fi if [[ ! -x "${VENV_PIP}" ]]; then echo "pip is missing in virtual environment: ${VENV_PIP}" >&2 exit 1 fi echo "[start.sh] Installing Python dependencies..." "${VENV_PIP}" install --upgrade pip "${VENV_PIP}" install -r "${REQUIREMENTS_FILE}" } run_privileged() { if [[ "${EUID}" -eq 0 ]]; then "$@" return fi if command -v sudo >/dev/null 2>&1; then sudo "$@" return fi echo "Need elevated privileges to run: $*" >&2 echo "Run as root or install 'sudo'." >&2 exit 1 } ensure_system_dependencies() { if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists libusb-1.0; then return fi echo "[start.sh] Installing system dependency: libusb-1.0 dev headers..." if command -v apt-get >/dev/null 2>&1; then run_privileged apt-get update run_privileged apt-get install -y pkg-config libusb-1.0-0-dev elif command -v dnf >/dev/null 2>&1; then run_privileged dnf install -y pkgconf-pkg-config libusb1-devel elif command -v yum >/dev/null 2>&1; then run_privileged yum install -y pkgconfig libusb1-devel elif command -v pacman >/dev/null 2>&1; then run_privileged pacman -Sy --needed pkgconf libusb elif command -v zypper >/dev/null 2>&1; then run_privileged zypper --non-interactive install pkg-config libusb-1_0-devel elif command -v brew >/dev/null 2>&1; then brew install pkg-config libusb else echo "Could not detect supported package manager." >&2 echo "Install manually: pkg-config and libusb development package." >&2 exit 1 fi if ! command -v pkg-config >/dev/null 2>&1 || ! pkg-config --exists libusb-1.0; then echo "libusb-1.0 development package is still unavailable after install attempt." >&2 exit 1 fi } ensure_usb_access_rules() { if [[ "$(uname -s)" != "Linux" ]]; then return fi if ! command -v udevadm >/dev/null 2>&1; then return fi local rule_file="/etc/udev/rules.d/99-radar-librevna.rules" local tmp_rule tmp_rule="$(mktemp)" cat > "${tmp_rule}" <<'EOF' # LibreVNA USB access for non-root users SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="564e", GROUP="plugdev", MODE="0660", TAG+="uaccess" SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="4121", GROUP="plugdev", MODE="0660", TAG+="uaccess" SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="4121", GROUP="plugdev", MODE="0660", TAG+="uaccess" EOF if [[ -f "${rule_file}" ]] && cmp -s "${tmp_rule}" "${rule_file}"; then rm -f "${tmp_rule}" return fi echo "[start.sh] Installing udev rule for LibreVNA USB access..." run_privileged install -m 0644 "${tmp_rule}" "${rule_file}" rm -f "${tmp_rule}" run_privileged udevadm control --reload-rules run_privileged udevadm trigger echo "[start.sh] udev rules updated. Reconnect USB device if it is already plugged in." } build_cpp_binaries() { local jobs jobs="${BUILD_JOBS:-$(nproc)}" echo "[start.sh] Building C++ binaries (jobs=${jobs})..." make -C "${PROJECT_ROOT}" -j"${jobs}" all } cleanup_known_shm() { echo "[start.sh] Cleaning known shared-memory segments..." rm -f /dev/shm/radar_raw /dev/shm/radar_preprocessed /dev/shm/radar_results || true } run_gui() { echo "[start.sh] Launching GUI..." exec "${VENV_PYTHON}" "${GUI_ENTRY}" } main() { parse_args "$@" check_environment ensure_system_dependencies ensure_usb_access_rules ensure_python_dependencies if ((CLEAN_SHM == 1)); then cleanup_known_shm fi if ((SKIP_BUILD == 0)); then build_cpp_binaries fi if ((BUILD_ONLY == 1)); then echo "[start.sh] Build completed." exit 0 fi run_gui } main "$@"