new kamil adc
This commit is contained in:
@@ -18,18 +18,21 @@ LOCK_FILE="/tmp/radar_system.lock"
|
||||
SKIP_BUILD=0
|
||||
BUILD_ONLY=0
|
||||
CLEAN_SHM=0
|
||||
KAMIL_ADC_MODE=0
|
||||
AUTO_START=0
|
||||
PRODUCER_ONLY=0
|
||||
HEADLESS=0
|
||||
# Acquisition device, detected from the active run config's radar.model. Drives
|
||||
# device-specific provisioning, dependencies, and which collector binary to build
|
||||
# — so every device launches the same way (no per-device flags).
|
||||
RADAR_MODEL=""
|
||||
|
||||
print_usage() {
|
||||
cat <<'EOF'
|
||||
Usage: ./start.sh [options]
|
||||
|
||||
Options:
|
||||
--kamil-adc Use the Raspberry Pi Kamil ADC profile
|
||||
--profile PATH Use a specific GUI/run config profile
|
||||
--profile PATH Use a specific GUI/run config profile (device is auto-detected
|
||||
from its radar.model)
|
||||
--auto-start Start the GUI pipeline automatically after launch
|
||||
--headless Run without a display (Qt offscreen platform) and apply
|
||||
the active radar config, then start the pipeline. Suitable
|
||||
@@ -55,9 +58,6 @@ parse_args() {
|
||||
--clean-shm)
|
||||
CLEAN_SHM=1
|
||||
;;
|
||||
--kamil-adc)
|
||||
KAMIL_ADC_MODE=1
|
||||
;;
|
||||
--profile)
|
||||
if (($# < 2)); then
|
||||
echo "--profile requires a path argument." >&2
|
||||
@@ -100,16 +100,6 @@ absolute_path() {
|
||||
}
|
||||
|
||||
resolve_profile_path() {
|
||||
if ((KAMIL_ADC_MODE == 1)); then
|
||||
if [[ -z "${PROFILE_PATH}" ]]; then
|
||||
if [[ -f "${PROJECT_ROOT}/run_config_kamil_adc.pi.json" ]]; then
|
||||
PROFILE_PATH="${PROJECT_ROOT}/run_config_kamil_adc.pi.json"
|
||||
else
|
||||
PROFILE_PATH="${PROJECT_ROOT}/run_config_kamil_adc.example.json"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${PROFILE_PATH}" ]]; then
|
||||
PROFILE_PATH="$(absolute_path "${PROFILE_PATH}")"
|
||||
if [[ ! -f "${PROFILE_PATH}" ]]; then
|
||||
@@ -119,6 +109,44 @@ resolve_profile_path() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Resolve the config that will actually be used (explicit --profile, else the
|
||||
# active run_config.json) and read its radar.model. Best-effort: any failure
|
||||
# falls back to 'librevna' (the full-provisioning superset), so detection can
|
||||
# never make a launch less safe. Uses system python3 (the venv may not exist yet).
|
||||
detect_radar_model() {
|
||||
local config_path="${PROFILE_PATH:-${PROJECT_ROOT}/run_config.json}"
|
||||
RADAR_MODEL="librevna"
|
||||
[[ -f "${config_path}" ]] || return
|
||||
local detected
|
||||
detected="$(python3 -c '
|
||||
import json, sys
|
||||
try:
|
||||
with open(sys.argv[1]) as handle:
|
||||
data = json.load(handle)
|
||||
radar = data.get("radar") if isinstance(data, dict) else {}
|
||||
model = radar.get("model") if isinstance(radar, dict) else None
|
||||
print(model or "librevna")
|
||||
except Exception:
|
||||
print("librevna")
|
||||
' "${config_path}" 2>/dev/null)" || detected=""
|
||||
[[ -n "${detected}" ]] && RADAR_MODEL="${detected}"
|
||||
echo "[start.sh] Detected radar model: ${RADAR_MODEL} (config: ${config_path})"
|
||||
}
|
||||
|
||||
# Acquisition producer for the active model, mirroring the process supervisor's
|
||||
# selection so --producer-only behaves identically to a full pipeline launch.
|
||||
producer_command() {
|
||||
local config_path="$1"
|
||||
case "${RADAR_MODEL}" in
|
||||
kamil_adc)
|
||||
printf '%s\0' "${PYTHON_CMD}" -m python_app.scripts.kamil_adc_raw_producer --config "${config_path}" ;;
|
||||
librevna_multi|sn9000)
|
||||
printf '%s\0' "${PYTHON_CMD}" -m python_app.scripts.matrix_raw_producer --config "${config_path}" ;;
|
||||
*)
|
||||
printf '%s\0' "${PROJECT_ROOT}/build/bin/sweep_orchestrator" --config "${config_path}" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
check_environment() {
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
echo "python3 is not installed or not found in PATH." >&2
|
||||
@@ -138,7 +166,8 @@ check_environment() {
|
||||
|
||||
ensure_python_dependencies() {
|
||||
local dependency_check
|
||||
if ((KAMIL_ADC_MODE == 1)); then
|
||||
if [[ "${RADAR_MODEL}" == "kamil_adc" ]]; then
|
||||
# Kamil ADC has no VISA dependency (it talks to its own L-Card collector).
|
||||
dependency_check='import numpy, serial, PyQt6, pyqtgraph, usb1'
|
||||
else
|
||||
dependency_check='import numpy, serial, PyQt6, pyqtgraph, usb1, pyvisa'
|
||||
@@ -260,14 +289,20 @@ EOF
|
||||
}
|
||||
|
||||
build_cpp_binaries() {
|
||||
if make -C "${PROJECT_ROOT}" -q all >/dev/null 2>&1; then
|
||||
local jobs
|
||||
jobs="${BUILD_JOBS:-$(nproc)}"
|
||||
# The Kamil ADC collector is an extra, device-specific binary built only for
|
||||
# that model; all models share the core pipeline binaries (`all`).
|
||||
local targets="all"
|
||||
if [[ "${RADAR_MODEL}" == "kamil_adc" ]]; then
|
||||
targets="all kamil_adc_collector"
|
||||
fi
|
||||
if make -C "${PROJECT_ROOT}" -q ${targets} >/dev/null 2>&1; then
|
||||
echo "[start.sh] C++ binaries are up to date; skipping build."
|
||||
return
|
||||
fi
|
||||
local jobs
|
||||
jobs="${BUILD_JOBS:-$(nproc)}"
|
||||
echo "[start.sh] Building C++ binaries (jobs=${jobs})..."
|
||||
make -C "${PROJECT_ROOT}" -j"${jobs}" all
|
||||
echo "[start.sh] Building C++ binaries (jobs=${jobs}, targets: ${targets})..."
|
||||
make -C "${PROJECT_ROOT}" -j"${jobs}" ${targets}
|
||||
}
|
||||
|
||||
cleanup_known_shm() {
|
||||
@@ -286,6 +321,17 @@ cleanup_known_shm() {
|
||||
|| true
|
||||
}
|
||||
|
||||
kill_stale_adc_collector() {
|
||||
# The L-Card ADC collector runs in its own session and can outlive a crashed
|
||||
# or force-killed run, holding the E-502 device and hanging the next start.
|
||||
# Kill any leftover hard before launching so acquisition always opens cleanly.
|
||||
if command -v pkill >/dev/null 2>&1; then
|
||||
if pkill -9 -f kamil_adc_collector 2>/dev/null; then
|
||||
echo "[start.sh] Killed leftover ADC collector process(es)."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
run_gui() {
|
||||
if [[ -n "${PROFILE_PATH}" ]]; then
|
||||
export RADAR_SYSTEM_PROFILE="${PROFILE_PATH}"
|
||||
@@ -311,18 +357,20 @@ run_gui() {
|
||||
}
|
||||
|
||||
run_producer_only() {
|
||||
if ((KAMIL_ADC_MODE != 1)); then
|
||||
echo "--producer-only currently requires --kamil-adc." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${PROFILE_PATH}" ]]; then
|
||||
echo "--producer-only requires a resolved config profile." >&2
|
||||
local config_path="${PROFILE_PATH:-${PROJECT_ROOT}/run_config.json}"
|
||||
if [[ ! -f "${config_path}" ]]; then
|
||||
echo "--producer-only needs a config (pass --profile, or create run_config.json)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export PYTHONPATH="${PROJECT_ROOT}${PYTHONPATH:+:${PYTHONPATH}}"
|
||||
echo "[start.sh] Starting Kamil ADC raw producer with profile: ${PROFILE_PATH}"
|
||||
exec "${PYTHON_CMD}" -m python_app.scripts.kamil_adc_raw_producer --config "${PROFILE_PATH}"
|
||||
# Run the acquisition producer the supervisor would pick for this model.
|
||||
local -a command=()
|
||||
while IFS= read -r -d '' token; do
|
||||
command+=("${token}")
|
||||
done < <(producer_command "${config_path}")
|
||||
echo "[start.sh] Starting ${RADAR_MODEL} producer: ${command[*]}"
|
||||
exec "${command[@]}"
|
||||
}
|
||||
|
||||
stop_headless_service() {
|
||||
@@ -345,7 +393,13 @@ verify_cpp_binaries() {
|
||||
# Guard the daemon's --skip-build path: refuse to run against a tree whose C++
|
||||
# binaries were never built, instead of failing obscurely at spawn time.
|
||||
local missing=0 bin
|
||||
for bin in data_processor data_preprocessor; do
|
||||
local required_bins="data_processor data_preprocessor"
|
||||
# Kamil ADC also needs its acquisition collector; other models use the C++
|
||||
# sweep_orchestrator, which `all` always builds.
|
||||
if [[ "${RADAR_MODEL}" == "kamil_adc" ]]; then
|
||||
required_bins="${required_bins} kamil_adc_collector"
|
||||
fi
|
||||
for bin in ${required_bins}; do
|
||||
if [[ ! -x "${PROJECT_ROOT}/build/bin/${bin}" ]]; then
|
||||
echo "Required binary missing or not executable: build/bin/${bin}" >&2
|
||||
missing=1
|
||||
@@ -373,10 +427,13 @@ main() {
|
||||
parse_args "$@"
|
||||
resolve_profile_path
|
||||
check_environment
|
||||
detect_radar_model
|
||||
# Skip first-time provisioning (build headers, USB udev rule) in headless
|
||||
# mode: the daemon runs unattended at boot as a non-root user and must not
|
||||
# block on sudo. A fresh machine is provisioned by one interactive launch.
|
||||
if ((KAMIL_ADC_MODE == 0 && HEADLESS == 0)); then
|
||||
# Also skip it for non-LibreVNA devices (e.g. Kamil ADC), which neither use
|
||||
# libusb directly nor need the LibreVNA USB access rule.
|
||||
if [[ "${RADAR_MODEL}" != "kamil_adc" ]] && ((HEADLESS == 0)); then
|
||||
ensure_system_dependencies
|
||||
ensure_usb_access_rules
|
||||
fi
|
||||
@@ -404,6 +461,8 @@ main() {
|
||||
stop_headless_service
|
||||
fi
|
||||
acquire_single_instance_lock
|
||||
# Clear any ADC collector wedged by a previous run before we launch a new one.
|
||||
kill_stale_adc_collector
|
||||
|
||||
if ((PRODUCER_ONLY == 1)); then
|
||||
run_producer_only
|
||||
|
||||
Reference in New Issue
Block a user