summaryrefslogtreecommitdiff
path: root/stacks/crdop/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'stacks/crdop/scripts')
-rwxr-xr-xstacks/crdop/scripts/apply-vendor-patches.sh36
-rwxr-xr-xstacks/crdop/scripts/build-crdop.sh48
-rwxr-xr-xstacks/crdop/scripts/crdopc171
-rwxr-xr-xstacks/crdop/scripts/import-ardopcf.sh17
-rwxr-xr-xstacks/crdop/scripts/install-crdop.sh16
-rwxr-xr-xstacks/crdop/scripts/refresh-vendor-ardopcf.sh26
-rwxr-xr-xstacks/crdop/scripts/sync-upstream.sh21
-rwxr-xr-xstacks/crdop/scripts/test-all.sh17
-rwxr-xr-xstacks/crdop/scripts/test-smoke.sh24
-rwxr-xr-xstacks/crdop/scripts/test-with-host.sh9
10 files changed, 385 insertions, 0 deletions
diff --git a/stacks/crdop/scripts/apply-vendor-patches.sh b/stacks/crdop/scripts/apply-vendor-patches.sh
new file mode 100755
index 0000000..da62bf5
--- /dev/null
+++ b/stacks/crdop/scripts/apply-vendor-patches.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+# Apply CRDOP patch series — only if vendor tree is still pristine upstream.
+# Released CRDOP commits ship vendor/ardopcf with patches already integrated.
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+VENDOR="${ROOT}/vendor/ardopcf"
+PATCH_DIR="${ROOT}/patches/ardopcf"
+STAMP="${VENDOR}/.crdop-patches-applied"
+MARKER="${VENDOR}/src/common/ARDOPC.c"
+
+if [[ ! -d "${VENDOR}/src" ]]; then
+ echo "error: ${VENDOR} missing" >&2
+ exit 1
+fi
+
+if grep -q 'CRDOP src/crdop_version.c' "${MARKER}" 2>/dev/null; then
+ date -u +'%Y-%m-%dT%H:%M:%SZ' > "${STAMP}"
+ echo "CRDOP vendor already integrated ($(cat "${STAMP}"))"
+ exit 0
+fi
+
+if [[ ! -d "${PATCH_DIR}" ]] || ! compgen -G "${PATCH_DIR}/*.patch" >/dev/null; then
+ echo "error: no patches in ${PATCH_DIR}" >&2
+ exit 1
+fi
+
+cd "${VENDOR}"
+for p in "${PATCH_DIR}"/*.patch; do
+ echo "applying ${p#${ROOT}/}"
+ git apply --check "${p}" 2>/dev/null || patch -p1 --forward -r - < "${p}"
+ git apply "${p}" 2>/dev/null || patch -p1 --forward < "${p}"
+done
+
+date -u +'%Y-%m-%dT%H:%M:%SZ' > "${STAMP}"
+echo "CRDOP patches applied ($(cat "${STAMP}"))"
diff --git a/stacks/crdop/scripts/build-crdop.sh b/stacks/crdop/scripts/build-crdop.sh
new file mode 100755
index 0000000..2253d5a
--- /dev/null
+++ b/stacks/crdop/scripts/build-crdop.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+# CRDOP unified build — vendor embedded; GCC, Clang, or MSVC via CMake.
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+BUILD="${CRDOP_BUILD_DIR:-${ROOT}/build}"
+TYPE="${CRDOP_BUILD_TYPE:-Release}"
+TOOLCHAIN="${CRDOP_TOOLCHAIN:-}"
+BUILD_TESTS="${CRDOP_BUILD_TESTS:-OFF}"
+RUN_TESTS="${CRDOP_RUN_TESTS:-OFF}"
+
+if [[ -f "${BUILD}/CMakeCache.txt" ]]; then
+ cached_home="$(grep -m1 '^CMAKE_HOME_DIRECTORY:INTERNAL=' "${BUILD}/CMakeCache.txt" 2>/dev/null | cut -d= -f2- || true)"
+ if [[ -n "${cached_home}" && "${cached_home}" != "${ROOT}" ]]; then
+ echo "CRDOP: removing stale CMake cache (was ${cached_home}, now ${ROOT})" >&2
+ rm -rf "${BUILD}"
+ fi
+fi
+
+"${ROOT}/scripts/import-ardopcf.sh"
+
+CMAKE_ARGS=(-B "${BUILD}" -DCMAKE_BUILD_TYPE="${TYPE}" -DCRDOP_BUILD_TESTS="${BUILD_TESTS}")
+
+if [[ -n "${TOOLCHAIN}" ]]; then
+ CMAKE_ARGS+=(-DCMAKE_TOOLCHAIN_FILE="${ROOT}/cmake/toolchains/${TOOLCHAIN}.cmake")
+fi
+
+if [[ -n "${CC:-}" ]]; then
+ CMAKE_ARGS+=(-DCMAKE_C_COMPILER="${CC}")
+fi
+if [[ -n "${CXX:-}" ]]; then
+ CMAKE_ARGS+=(-DCMAKE_CXX_COMPILER="${CXX}")
+fi
+
+cmake "${ROOT}" "${CMAKE_ARGS[@]}"
+cmake --build "${BUILD}" -j"$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)"
+
+"${ROOT}/scripts/test-smoke.sh"
+
+if [[ "${BUILD_TESTS}" == "ON" || "${BUILD_TESTS}" == "1" || "${RUN_TESTS}" == "ON" || "${RUN_TESTS}" == "1" ]]; then
+ if [[ -f "${BUILD}/CTestTestfile.cmake" ]]; then
+ ctest --test-dir "${BUILD}" --output-on-failure
+ else
+ echo "NOTE: CRDOP_BUILD_TESTS requested but no CTest targets (install libcmocka-dev?)" >&2
+ fi
+fi
+
+echo "CRDOP $(cat "${ROOT}/VERSION") built: ${BUILD}/crdopc"
diff --git a/stacks/crdop/scripts/crdopc b/stacks/crdop/scripts/crdopc
new file mode 100755
index 0000000..2fc82bd
--- /dev/null
+++ b/stacks/crdop/scripts/crdopc
@@ -0,0 +1,171 @@
+#!/usr/bin/env bash
+# CRDOP launcher — native MAX25-SoftModem (audio-dummyd / M25 host).
+# ARDOP is a separate optional MAX25-Stack plugin — not part of CRDOP.
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+MAX25_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+PREFIX_SHARE="${SCRIPT_DIR}/../share/crdop"
+
+if [[ -n "${CRDOP_INI:-}" ]]; then
+ INI="${CRDOP_INI}"
+elif [[ -f "${HOME}/.config/crdop/crdop.ini" ]]; then
+ INI="${HOME}/.config/crdop/crdop.ini"
+elif [[ -f "${PREFIX_SHARE}/crdop.ini.example" ]]; then
+ INI="${PREFIX_SHARE}/crdop.ini.example"
+else
+ INI="${ROOT}/share/crdop.ini.example"
+fi
+
+ini_get() {
+ local section="$1" key="$2" default="$3" file="$4"
+ [[ -f "${file}" ]] || { echo "${default}"; return; }
+ awk -F= -v sec="[${section}]" -v k="${key}" -v d="${default}" '
+ $0 ~ /^[[:space:]]*[#;]/ { next }
+ $1 ~ /^\[/ { cur=$0; gsub(/[][]/, "", cur); next }
+ cur == substr(sec, 2, length(sec)-2) {
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1)
+ if ($1 == k) {
+ v=$2; gsub(/^[[:space:]]+|[[:space:]]+$/, "", v); gsub(/^"|"$/, "", v)
+ print v; found=1; exit
+ }
+ }
+ END { if (!found) print d }
+ ' "${file}"
+}
+
+resolve_ini() {
+ if [[ ! -f "${INI}" && -f "${ROOT}/share/crdop.ini.example" ]]; then
+ INI="${ROOT}/share/crdop.ini.example"
+ fi
+}
+
+normalize_profile() {
+ case "${1,,}" in
+ cb|dual|amateur) echo "${1,,}" ;;
+ *)
+ echo "warn: unknown radio_profile '${1}', using cb" >&2
+ echo "cb"
+ ;;
+ esac
+}
+
+normalize_duplex() {
+ case "${1,,}" in
+ full|half) echo "${1,,}" ;;
+ *)
+ echo "warn: unknown duplex '${1}', using half" >&2
+ echo "half"
+ ;;
+ esac
+}
+
+enforce_host_audio() {
+ local backend
+ backend="$(ini_get audio backend "" "${INI}")"
+ if [[ -z "${backend}" ]]; then
+ case "$(uname -s)" in
+ FreeBSD) backend="oss" ;;
+ *) backend="alsa" ;;
+ esac
+ fi
+ if [[ "${backend}" == "alsa" ]]; then
+ local no_pulse
+ no_pulse="$(ini_get audio no_pulse yes "${INI}")"
+ case "${no_pulse,,}" in
+ 1|yes|true|on)
+ unset PULSE_SERVER PULSE_COOKIE PIPEWIRE_RUNTIME_DIR 2>/dev/null || true
+ export PULSE_SERVER=
+ export ALSA_CARD="${ALSA_CARD:-}"
+ ;;
+ esac
+ fi
+}
+
+audio_cli_args() {
+ local cap pb backend
+ backend="$(ini_get audio backend "" "${INI}")"
+ if [[ -z "${backend}" ]]; then
+ case "$(uname -s)" in
+ FreeBSD) backend="oss" ;;
+ *) backend="alsa" ;;
+ esac
+ fi
+ cap="$(ini_get audio capture "" "${INI}")"
+ pb="$(ini_get audio playback "" "${INI}")"
+ local args=()
+ [[ -n "${cap}" ]] && args+=("${cap}")
+ [[ -n "${pb}" ]] && args+=("${pb}")
+ if [[ ${#args[@]} -gt 0 ]]; then
+ if [[ "${backend}" == "alsa" ]]; then
+ for a in "${args[@]}"; do
+ if [[ "${a}" == *pulse* || "${a}" == *pipewire* || "${a}" == default ]]; then
+ echo "error: audio device '${a}' — use hw:/plughw: kernel ALSA" >&2
+ exit 1
+ fi
+ done
+ fi
+ printf '%s\n' "${args[@]}"
+ elif [[ "${backend}" == "oss" ]]; then
+ echo "/dev/dsp"
+ echo "/dev/dsp"
+ fi
+}
+
+find_audio_dummyd() {
+ for candidate in \
+ "${MAX25_ROOT}/build/bin/audio-dummyd" \
+ "${MAX25_ROOT}/stacks/crdop/tools/audio-dummyd" \
+ "$(command -v audio-dummyd 2>/dev/null || true)"; do
+ [[ -n "${candidate}" && -x "${candidate}" ]] || continue
+ echo "${candidate}"
+ return
+ done
+ return 1
+}
+
+resolve_ini
+
+if [[ -f "${PREFIX_SHARE}/VERSION" ]]; then
+ CRDOP_VERSION="$(tr -d '[:space:]' < "${PREFIX_SHARE}/VERSION")"
+elif [[ -f "${ROOT}/VERSION" ]]; then
+ CRDOP_VERSION="$(tr -d '[:space:]' < "${ROOT}/VERSION")"
+else
+ CRDOP_VERSION="CUR999"
+fi
+
+PROFILE="$(normalize_profile "$(ini_get profile radio_profile cb "${INI}")")"
+DUPLEX="$(normalize_duplex "$(ini_get modem duplex half "${INI}")")"
+MYCALL="$(ini_get mycall call NOCALL-0 "${INI}")"
+HOST_PORT="$(ini_get host port 8515 "${INI}")"
+DATA_PORT=$((HOST_PORT + 1))
+SAMPLE_RATE="$(ini_get audio sample_rate 48000 "${INI}")"
+
+if [[ $# -ge 1 && "${1}" =~ ^[0-9]+$ ]]; then
+ HOST_PORT="$1"
+ DATA_PORT=$((HOST_PORT + 1))
+ shift
+fi
+
+mapfile -t AUDIO_ARGS < <(audio_cli_args || true)
+enforce_host_audio
+
+DUMMY="$(find_audio_dummyd || true)"
+if [[ -z "${DUMMY}" ]]; then
+ echo "error: audio-dummyd not found — build MAX25 or set path" >&2
+ exit 1
+fi
+
+DUMMY_ARGS=(--ctrl-port "${HOST_PORT}" --data-port "${DATA_PORT}" -r "${SAMPLE_RATE}")
+if [[ ${#AUDIO_ARGS[@]} -ge 1 ]]; then
+ DUMMY_ARGS+=(-D "${AUDIO_ARGS[0]}")
+fi
+if [[ ${#AUDIO_ARGS[@]} -ge 2 ]]; then
+ DUMMY_ARGS+=(-P "${AUDIO_ARGS[1]}")
+fi
+
+AUDIO_BACKEND="$(ini_get audio backend "" "${INI}")"
+[[ -z "${AUDIO_BACKEND}" ]] && case "$(uname -s)" in FreeBSD) AUDIO_BACKEND="oss" ;; *) AUDIO_BACKEND="alsa" ;; esac
+echo "CRDOP ${CRDOP_VERSION} profile=${PROFILE} duplex=${DUPLEX} ctrl=:${HOST_PORT} data=:${DATA_PORT} call=${MYCALL} audio=${AUDIO_BACKEND} (native M25)"
+exec "${DUMMY}" "${DUMMY_ARGS[@]}" "$@"
diff --git a/stacks/crdop/scripts/import-ardopcf.sh b/stacks/crdop/scripts/import-ardopcf.sh
new file mode 100755
index 0000000..26ac003
--- /dev/null
+++ b/stacks/crdop/scripts/import-ardopcf.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# Verify vendor/ardopcf is present (embedded in repo — no submodule/download).
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+VENDOR="${ROOT}/vendor/ardopcf"
+
+if [[ ! -f "${VENDOR}/LICENSE" || ! -f "${VENDOR}/src/common/ARDOPC.c" ]]; then
+ echo "error: vendor/ardopcf missing — clone CRDOP with full tree or run scripts/refresh-vendor-ardopcf.sh" >&2
+ exit 1
+fi
+
+if ! grep -q 'CRDOP src/crdop_version.c' "${VENDOR}/src/common/ARDOPC.c" 2>/dev/null; then
+ echo "warn: vendor/ardopcf may need CRDOP patches — run scripts/apply-vendor-patches.sh" >&2
+fi
+
+echo "vendor/ardopcf OK (embedded, commit $(grep '^commit=' "${ROOT}/vendor/ardopcf.ref" 2>/dev/null | cut -d= -f2 || echo unknown))"
diff --git a/stacks/crdop/scripts/install-crdop.sh b/stacks/crdop/scripts/install-crdop.sh
new file mode 100755
index 0000000..23f631c
--- /dev/null
+++ b/stacks/crdop/scripts/install-crdop.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+# Build and install CRDOP to PREFIX (default /usr/local).
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+PREFIX="${CRDOP_PREFIX:-/usr/local}"
+BUILD="${CRDOP_BUILD_DIR:-${ROOT}/build}"
+
+"${ROOT}/scripts/build-crdop.sh"
+
+cmake --install "${BUILD}" --prefix "${PREFIX}"
+
+echo "Installed:"
+echo " ${PREFIX}/bin/crdopc — modem binary"
+echo " ${PREFIX}/bin/crdop — profile launcher"
+echo " ${PREFIX}/share/crdop/ — INI examples, VERSION"
diff --git a/stacks/crdop/scripts/refresh-vendor-ardopcf.sh b/stacks/crdop/scripts/refresh-vendor-ardopcf.sh
new file mode 100755
index 0000000..b9d2f93
--- /dev/null
+++ b/stacks/crdop/scripts/refresh-vendor-ardopcf.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+# Maintainer tool: re-download upstream ardopcf and re-apply CRDOP patches.
+# Normal builds use the committed vendor/ardopcf tree — no network required.
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+VENDOR="${ROOT}/vendor/ardopcf"
+REF_FILE="${ROOT}/vendor/ardopcf.ref"
+URL="${CRDOP_UPSTREAM_URL:-https://github.com/pflarue/ardop.git}"
+PIN="$(grep '^commit_full=' "${REF_FILE}" 2>/dev/null | cut -d= -f2- | head -1)"
+PIN="${PIN:-$(grep '^commit=' "${REF_FILE}" 2>/dev/null | cut -d= -f2- | head -1)}"
+
+echo "This replaces vendor/ardopcf from upstream. Press Ctrl+C to abort."
+sleep 2
+
+rm -rf "${VENDOR}"
+git clone --depth 1 --branch master "${URL}" "${VENDOR}"
+if [[ -n "${PIN}" ]]; then
+ git -C "${VENDOR}" fetch --depth 1 origin "${PIN}"
+ git -C "${VENDOR}" checkout "${PIN}"
+fi
+rm -rf "${VENDOR}/.git"
+rm -f "${VENDOR}/.crdop-patches-applied"
+
+"${ROOT}/scripts/apply-vendor-patches.sh"
+echo "Done. Review diff, update vendor/ardopcf.ref if needed, commit vendor/ardopcf/"
diff --git a/stacks/crdop/scripts/sync-upstream.sh b/stacks/crdop/scripts/sync-upstream.sh
new file mode 100755
index 0000000..a1353ad
--- /dev/null
+++ b/stacks/crdop/scripts/sync-upstream.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+# Test whether CRDOP patches still apply to fresh upstream (maintainers).
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+TMP="$(mktemp -d)"
+trap 'rm -rf "${TMP}"' EXIT
+
+REF="${ROOT}/vendor/ardopcf.ref"
+PIN="$(grep '^commit_full=' "${REF}" | cut -d= -f2-)"
+URL="https://github.com/pflarue/ardop.git"
+
+git clone --depth 1 "${URL}" "${TMP}/ardopcf"
+git -C "${TMP}/ardopcf" fetch --depth 1 origin "${PIN}"
+git -C "${TMP}/ardopcf" checkout "${PIN}"
+
+for p in "${ROOT}"/patches/ardopcf/*.patch; do
+ git -C "${TMP}/ardopcf" apply --check "${p}"
+done
+
+echo "OK: patches apply to upstream ${PIN}"
diff --git a/stacks/crdop/scripts/test-all.sh b/stacks/crdop/scripts/test-all.sh
new file mode 100755
index 0000000..f12181e
--- /dev/null
+++ b/stacks/crdop/scripts/test-all.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# Smoke test + optional upstream cmocka unit tests (CTest).
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+BUILD="${CRDOP_BUILD_DIR:-${ROOT}/build}"
+
+export CRDOP_BUILD_TESTS="${CRDOP_BUILD_TESTS:-ON}"
+export CRDOP_RUN_TESTS="${CRDOP_RUN_TESTS:-ON}"
+
+"${ROOT}/scripts/build-crdop.sh"
+
+if [[ ! -f "${BUILD}/CTestTestfile.cmake" ]]; then
+ echo "NOTE: unit tests not built (libcmocka missing or unsupported platform)" >&2
+fi
+
+echo "OK: CRDOP test-all finished"
diff --git a/stacks/crdop/scripts/test-smoke.sh b/stacks/crdop/scripts/test-smoke.sh
new file mode 100755
index 0000000..6ee6eff
--- /dev/null
+++ b/stacks/crdop/scripts/test-smoke.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+REPO="$(cd "${ROOT}/../.." && pwd)"
+BIN="${CRDOP_BIN:-${ROOT}/build/crdopc}"
+
+if [[ ! -x "${BIN}" ]]; then
+ for alt in "${REPO}/build/bin/crdopc" "${REPO}/build/crdop/crdopc"; do
+ if [[ -x "${alt}" ]]; then
+ BIN="${alt}"
+ break
+ fi
+ done
+fi
+VER="$(tr -d '[:space:]' < "${ROOT}/VERSION")"
+
+[[ -x "${BIN}" ]] || { echo "FAIL: ${BIN} missing" >&2; exit 1; }
+
+OUT="$("${BIN}" -h 2>&1 || true)"
+echo "${OUT}" | grep -qi crdopc || { echo "FAIL: crdopc branding"; exit 1; }
+echo "${OUT}" | grep -q "${VER}" || { echo "FAIL: expected version ${VER}"; exit 1; }
+
+echo "OK: CRDOP ${VER} smoke test passed"
diff --git a/stacks/crdop/scripts/test-with-host.sh b/stacks/crdop/scripts/test-with-host.sh
new file mode 100755
index 0000000..f14baac
--- /dev/null
+++ b/stacks/crdop/scripts/test-with-host.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# P1 — loopback host integration (placeholder).
+set -euo pipefail
+
+echo "Host loopback test not automated yet."
+echo " 1. ./scripts/crdopc"
+echo " 2. Connect M25/KISS host client to 127.0.0.1:8515"
+echo " 3. INITIALIZE / PROTOCOLMODE KISS / LISTEN — verify STATUS ready"
+exit 0
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com