Files
Pluto-SDR/scripts/test_arq.sh
2026-07-16 14:30:17 +03:00

144 lines
8.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# test_arq.sh — e2e-тест ARQ и EOF-хендшейка (этап 3, §10 п.7, техдолг #6).
#
# Отличие от test_fdd_fwd.sh: там успех = md5 совпал, а завершение обеих сторон
# организует скрипт (killall + сторожевой kill). Здесь проверяется сам ПРОТОКОЛ:
# • B, собрав файл целиком, поднимает FB_FLAG_COMPLETE в STATUS и выходит САМ;
# • A ждёт этот COMPLETE и возвращает 0; не дождался за 10 с → exit 2.
# Поэтому сторожевой kill приёмника = провал (B не смог завершиться сам), а
# ненулевой код A — не аварийная ситуация, а штатный отчёт «файл не подтверждён».
#
# Использование: scripts/test_arq.sh [ipA] [ipB] [размер_КБ]
# SIZE_KB=10240 — длинный прогон (или третьим позиционным аргументом)
# TXGAIN=-45 — стресс: высокий PER, ARQ обязан вытянуть md5
# NEGATIVE=1 FBGAIN=-89 — обратный канал заглушён: успех = A НЕ подтвердил
# FBGAIN (деф. 15) — середина рабочего окна 20…10 (README §12.8)
# FBFREQ, PAUSE, TXGAIN, FEC, GROUPFEC — как в test_fdd_fwd.sh
# ВНИМАНИЕ: сначала кабель + аттенюатор 3040 дБ!
set -euo pipefail
source "$(dirname "$0")/../toolchain.env"
A="${1:-$PLUTO_A}" # передатчик данных (TX 915 / fb-RX 868)
B="${2:-$PLUTO_B}" # приёмник данных (RX 915 / fb-TX 868)
SIZE_KB="${3:-${SIZE_KB:-1024}}"
FREQ=915000000
FBFREQ="${FBFREQ:-868000000}"
RATE=1920000 # НЕ поднимать: бюджет CPU, docs/BENCHMARK.md
BW=1500000
TXGAIN="${TXGAIN:--30}"
FBGAIN="${FBGAIN:--15}" # штатный обратный канал; 89 ≈ выкл (NEGATIVE)
FEC="${FEC:-rs8}"
PAUSE="${PAUSE:-4000}" # §12.7: соук/маргинальный канал — только 4000, не 3000
GROUPFEC="${GROUPFEC:-1}"
NEGATIVE="${NEGATIVE:-0}"
[ "$GROUPFEC" = 1 ] && GFLAG="-G" || GFLAG=""
# +40 (против +20 у test_fdd_fwd): END-фаза до 10 с плюс запас на ретрансмиты.
TIMEOUT=$(( SIZE_KB / 85 + 40 ))
if command -v sshpass >/dev/null; then
SSH=(sshpass -p "$PLUTO_PASS" ssh -o StrictHostKeyChecking=no)
SCP=(sshpass -p "$PLUTO_PASS" scp -O -o StrictHostKeyChecking=no)
else
SSH=(ssh); SCP=(scp -O)
echo "подсказка: apt install sshpass — и пароль вводить не придётся"
fi
echo "=== ARQ e2e: A=$A (TX $((FREQ/1000000))/fb-RX $((FBFREQ/1000000))) → B=$B ==="
echo " ${SIZE_KB} КБ TXGAIN=$TXGAIN FBGAIN=$FBGAIN PAUSE=$PAUSE GROUPFEC=$GROUPFEC NEGATIVE=$NEGATIVE"
echo "=== [1/6] Тестовый файл ${SIZE_KB} КБ ==="
dd if=/dev/urandom of=/tmp/arq_test.bin bs=1024 count="$SIZE_KB" status=none
MD5_SRC=$(md5sum /tmp/arq_test.bin | cut -d' ' -f1)
echo "md5: $MD5_SRC"
echo "=== [2/6] Зачистка хвостов на обеих платах ==="
# Остаточный процесс держит RX-DMA → пустой out.bin (грабля из README §12.6).
"${SSH[@]}" "root@$A" "killall -9 transmitter receiver 2>/dev/null || true"
"${SSH[@]}" "root@$B" "killall -9 transmitter receiver 2>/dev/null || true"
echo "=== [3/6] Заливка файла на Pluto A ($A) ==="
"${SCP[@]}" /tmp/arq_test.bin "root@$A:/tmp/"
echo "=== [4/6] Старт приёмника на Pluto B ($B), сторож ${TIMEOUT}с ==="
# busybox без `timeout` → сторожевой sleep+kill на плате. Штатно B выходит САМ по
# COMPLETE задолго до сторожа; сторож снимается сразу после выхода B, иначе ssh
# висел бы до конца sleep и скрывал реальное время завершения.
"${SSH[@]}" "root@$B" \
"rm -f /tmp/out.bin; /tmp/receiver -f $FREQ -r $RATE -b $BW -c $FEC \
-F $FBFREQ -g $FBGAIN -u local: > /tmp/out.bin 2>/tmp/rx_arq.log & \
RXP=\$!; (sleep $TIMEOUT; kill \$RXP 2>/dev/null) & WDP=\$!; \
wait \$RXP; RC=\$?; kill \$WDP 2>/dev/null; exit \$RC" &
RX_PID=$!
sleep 4 # дать RX прогреться (FDD-инициализация двух трактов дольше)
echo "=== [5/6] Передача с Pluto A + EOF-хендшейк ==="
TX_RC=0
"${SSH[@]}" "root@$A" \
"cat /tmp/arq_test.bin | /tmp/transmitter -f $FREQ -r $RATE -b $BW \
-g $TXGAIN -p $PAUSE -c $FEC $GFLAG -F $FBFREQ -u local: 2>/tmp/tx_arq.log" \
|| TX_RC=$?
echo "код возврата передатчика: $TX_RC (0 = COMPLETE подтверждён, 2 = таймаут)"
wait "$RX_PID" 2>/dev/null || true # B выходит сам либо снимается сторожем
"${SSH[@]}" "root@$B" "killall receiver 2>/dev/null || true" # страховка
echo "=== [6/6] Сверка ==="
MD5_DST=$("${SSH[@]}" "root@$B" "md5sum /tmp/out.bin 2>/dev/null | cut -d' ' -f1" || echo ет_файла")
SIZE_DST=$("${SSH[@]}" "root@$B" "wc -c < /tmp/out.bin 2>/dev/null" || echo 0)
echo "источник: $MD5_SRC ($((SIZE_KB*1024)) байт)"
echo "приёмник: $MD5_DST ($SIZE_DST байт)"
# Самозавершение B: маркер печатается ТОЛЬКО когда COMPLETE ушёл в эфир и B
# закрывается штатно. Нет маркера → B досидел до сторожевого kill = провал 3a.
SELF_DONE=0
"${SSH[@]}" "root@$B" "grep -q 'завершаю приём' /tmp/rx_arq.log 2>/dev/null" && SELF_DONE=1 || true
echo "--- Хендшейк ---"
"${SSH[@]}" "root@$B" "grep ARQ /tmp/rx_arq.log 2>/dev/null | sed 's/^/B: /'" || true
"${SSH[@]}" "root@$A" "grep ARQ /tmp/tx_arq.log 2>/dev/null | sed 's/^/A: /'" || true
echo "B самозавершился: $([ "$SELF_DONE" = 1 ] && echo да || echo 'НЕТ (снят сторожем)')"
echo "--- Обратный канал STATUS (B→A) ---"
SENT=$("${SSH[@]}" "root@$B" "grep -o 'отправлено STATUS: [0-9]*' /tmp/rx_arq.log 2>/dev/null | grep -o '[0-9]*'" || echo "")
RECV=$("${SSH[@]}" "root@$A" "grep -o 'принято STATUS: [0-9]*' /tmp/tx_arq.log 2>/dev/null | grep -o '[0-9]*'" || echo "")
SENT=${SENT:-0}; RECV=${RECV:-0}
if [ "$SENT" -gt 0 ]; then
echo "B отправил: $SENT | A принял: $RECV | доставка ≈ $(( RECV * 100 / SENT ))%"
else
echo "B отправил: $SENT | A принял: $RECV"
fi
echo "--- RX-статистика (PER) ---"
"${SSH[@]}" "root@$B" "grep '^Кадров:' /tmp/rx_arq.log 2>/dev/null | tail -1" || true
# Ретрансмиты — главная метрика 3b: сырой PER остаётся, но md5 обязан сойтись.
"${SSH[@]}" "root@$A" "grep 'ретрансмитов' /tmp/tx_arq.log 2>/dev/null | tail -1 | sed 's/^/A: /'" || true
echo "=== РЕЗУЛЬТАТ ==="
if [ "$NEGATIVE" = 1 ]; then
# Обратный канал заглушён: B файл соберёт и выйдет сам, но подтвердить A
# некому. Проверяем именно честность отказа — раньше A рапортовал успех.
if [ "$TX_RC" != 0 ]; then
echo "OK (негатив) — A не получил COMPLETE и честно вышел с кодом $TX_RC"
else
echo "FAIL (негатив) — A вернул 0 без обратного канала: хендшейк фиктивен"
exit 1
fi
else
if [ "$MD5_SRC" = "$MD5_DST" ] && [ "$SELF_DONE" = 1 ] && [ "$TX_RC" = 0 ]; then
echo "OK — md5 совпал, B завершился по COMPLETE, A подтвердил"
else
echo "FAIL: md5 $([ "$MD5_SRC" = "$MD5_DST" ] && echo ок || echo РАСХОЖДЕНИЕ) |" \
"самозавершение $([ "$SELF_DONE" = 1 ] && echo ок || echo НЕТ) | код A $TX_RC"
echo "Диагностика:"
echo " - md5 разошёлся при коде A=0 → COMPLETE поднят рано (проверь miss/next_out)."
echo " - код A=2 при целом md5 → COMPLETE не долетел: подними FBGAIN к 10."
echo " - размер 0 → RX не декодит: остаточный процесс на B или самоглушение."
echo " - GROUPFEC=1: ARQ там пока выключен (этап 3c) → потеря кадра даёт код A=2."
echo " - 'NACK вне окна' > 0 на B → дыра пережила TX-окно (2048 кадров ≈ 21 с)."
echo "--- хвост rx_arq.log (B) ---"
"${SSH[@]}" "root@$B" "tail -8 /tmp/rx_arq.log" || true
exit 1
fi
fi