доработка в части приема и передачи
This commit is contained in:
@@ -14,7 +14,8 @@ SIZE_KB="${3:-1024}"
|
||||
FREQ=915000000
|
||||
RATE=1920000 # НЕ поднимать: бюджет CPU, docs/BENCHMARK.md
|
||||
BW=1500000
|
||||
TXGAIN=-40 # для кабеля; антенны: −20…0
|
||||
TXGAIN="${TXGAIN:--30}" # OTA: переопредели, напр. TXGAIN=-20 scripts/test_file_link.sh
|
||||
FEC="${FEC:-rs8}" # rs8 | none — фаза 3.2 гоняет сначала none, потом rs8
|
||||
TIMEOUT=$(( SIZE_KB / 100 + 30 )) # ~160 КБ/с + запас
|
||||
|
||||
if command -v sshpass >/dev/null; then
|
||||
@@ -36,14 +37,14 @@ echo "=== [2/5] Заливка файла на Pluto A ($A) ==="
|
||||
echo "=== [3/5] Старт приёмника на Pluto B ($B), таймаут ${TIMEOUT}с ==="
|
||||
"${SSH[@]}" "root@$B" \
|
||||
"rm -f /tmp/out.bin; timeout $TIMEOUT /tmp/receiver -f $FREQ -r $RATE \
|
||||
-b $BW -c rs8 -u local: > /tmp/out.bin 2>/tmp/rx.log" &
|
||||
-b $BW -c $FEC -u local: > /tmp/out.bin 2>/tmp/rx.log" &
|
||||
RX_PID=$!
|
||||
sleep 3 # дать RX прогреться до старта TX
|
||||
|
||||
echo "=== [4/5] Передача с Pluto A ==="
|
||||
"${SSH[@]}" "root@$A" \
|
||||
"cat /tmp/link_test.bin | /tmp/transmitter -f $FREQ -r $RATE -b $BW \
|
||||
-g $TXGAIN -p 0 -c rs8 -u local: 2>/tmp/tx.log"
|
||||
-g $TXGAIN -p 0 -c $FEC -u local: 2>/tmp/tx.log"
|
||||
sleep 3 # дохлебать хвост эфира
|
||||
"${SSH[@]}" "root@$B" "killall receiver 2>/dev/null || true"
|
||||
wait "$RX_PID" 2>/dev/null || true
|
||||
|
||||
@@ -13,9 +13,11 @@ extern volatile sig_atomic_t stop;
|
||||
|
||||
static uint64_t calls = 0, hdr_ok = 0, crc_raw = 0;
|
||||
static uint64_t rs_saved = 0, crc_final = 0, total_bytes = 0;
|
||||
static uint64_t rs_fail = 0, lost = 0;
|
||||
static uint32_t last_seq = 0;
|
||||
static int seq_init = 0;
|
||||
static float rssi = -100, evm = 0;
|
||||
static float cfo_est = 0.0f;
|
||||
static nco_crcf nco = NULL;
|
||||
static fec decoder = NULL;
|
||||
static int fec_on = 0;
|
||||
static liquid_float_complex frame[MAX_FRAME_SAMPLES];
|
||||
@@ -29,16 +31,28 @@ static int callback(unsigned char *hdr, int hdr_valid,
|
||||
rssi = stats.rssi;
|
||||
evm = stats.evm;
|
||||
|
||||
// Обновляем CFO только если NCO инициализирован
|
||||
if (nco && fabsf(stats.cfo) < CFO_MAX_VALID) {
|
||||
// CFO — только телеметрия: ofdmflexframesync сам компенсирует смещение
|
||||
// по преамбуле. Внешний NCO-контур убран (техдолг #4): с set_phase(0)
|
||||
// на границах буферов он рвал фазу посреди кадра.
|
||||
if (fabsf(stats.cfo) < CFO_MAX_VALID)
|
||||
cfo_est = CFO_FILTER_ALPHA * cfo_est + (1.0f - CFO_FILTER_ALPHA) * stats.cfo;
|
||||
nco_crcf_set_frequency(nco, cfo_est);
|
||||
}
|
||||
|
||||
if (!hdr_valid) return 0;
|
||||
hdr_ok++;
|
||||
if (pay_valid) crc_raw++;
|
||||
|
||||
// Детектор пропусков seq (техдолг #6, минимум): потерю кадра в эфире
|
||||
// надо отличать от порчи payload. ARQ нет — только лог дыры.
|
||||
uint32_t seq = ((uint32_t)hdr[2] << 24) | ((uint32_t)hdr[3] << 16) |
|
||||
((uint32_t)hdr[4] << 8) | hdr[5];
|
||||
if (seq_init && seq > last_seq + 1) {
|
||||
lost += seq - last_seq - 1;
|
||||
fprintf(stderr, "\n[ПОТЕРЯ] seq %u→%u, дыра %u кадров\n",
|
||||
last_seq, seq, seq - last_seq - 1);
|
||||
}
|
||||
last_seq = seq;
|
||||
seq_init = 1;
|
||||
|
||||
uint16_t original_len = (hdr[6] << 8) | hdr[7];
|
||||
uint8_t last_block_bytes = hdr[10];
|
||||
|
||||
@@ -49,26 +63,38 @@ static int callback(unsigned char *hdr, int hdr_valid,
|
||||
if (nblocks == 0 || nblocks > 4096 / RS_DATA) return 0;
|
||||
|
||||
uint8_t recovered[4096];
|
||||
int rs_ok = 1;
|
||||
for (size_t b = 0; b < nblocks; b++) {
|
||||
if (fec_decode(decoder, RS_DATA, pay + b * RS_ENC,
|
||||
recovered + b * RS_DATA) != 0) {
|
||||
rs_ok = 0; break;
|
||||
}
|
||||
// fec_decode() liquid всегда "успешен" — код возврата бесполезен.
|
||||
fec_decode(decoder, RS_DATA, pay + b * RS_ENC,
|
||||
recovered + b * RS_DATA);
|
||||
}
|
||||
if (rs_ok) {
|
||||
if (!pay_valid) rs_saved++;
|
||||
size_t out_len = (nblocks - 1) * RS_DATA +
|
||||
(last_block_bytes > 0 ? last_block_bytes : RS_DATA);
|
||||
if (out_len > nblocks * RS_DATA) out_len = nblocks * RS_DATA;
|
||||
if (out_len > original_len) out_len = original_len;
|
||||
if (out_len > 0) {
|
||||
fwrite(recovered, 1, out_len, stdout);
|
||||
fflush(stdout);
|
||||
total_bytes += out_len;
|
||||
crc_final++;
|
||||
}
|
||||
|
||||
// Декодировано framed_len = original_len + 4 (CRC32) байт. Настоящий
|
||||
// критерий целостности — CRC поверх декода (техдолг #3): fec_decode
|
||||
// отдаёт мусор молча, поэтому проверяем сами и мусор в файл не пишем.
|
||||
size_t framed_len = (nblocks - 1) * RS_DATA +
|
||||
(last_block_bytes > 0 ? last_block_bytes : RS_DATA);
|
||||
if (framed_len > nblocks * RS_DATA) framed_len = nblocks * RS_DATA;
|
||||
|
||||
if (original_len == 0 || (size_t)original_len + 4 > framed_len) {
|
||||
rs_fail++;
|
||||
return 0;
|
||||
}
|
||||
unsigned int rx_crc = crc_generate_key(LIQUID_CRC_32, recovered, original_len);
|
||||
unsigned int tx_crc = ((uint32_t)recovered[original_len] << 24) |
|
||||
((uint32_t)recovered[original_len + 1] << 16) |
|
||||
((uint32_t)recovered[original_len + 2] << 8) |
|
||||
recovered[original_len + 3];
|
||||
if (rx_crc != tx_crc) {
|
||||
rs_fail++; // RS не справился, кадр битый — не пишем
|
||||
return 0;
|
||||
}
|
||||
if (!pay_valid) rs_saved++; // liquid CRC по payload не прошёл, а наш
|
||||
// CRC поверх RS — да: значит RS реально спас
|
||||
fwrite(recovered, 1, original_len, stdout);
|
||||
fflush(stdout);
|
||||
total_bytes += original_len;
|
||||
crc_final++;
|
||||
} else if (pay_valid && original_len > 0 && original_len <= pay_len) {
|
||||
fwrite(pay, 1, original_len, stdout);
|
||||
fflush(stdout);
|
||||
@@ -141,20 +167,11 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация NCO для частотной коррекции
|
||||
nco = nco_crcf_create(LIQUID_NCO);
|
||||
if (!nco) {
|
||||
fprintf(stderr, "Ошибка создания NCO\n");
|
||||
} else {
|
||||
nco_crcf_set_frequency(nco, 0.0f);
|
||||
}
|
||||
|
||||
ofdmflexframesync fs = ofdmflexframesync_create(
|
||||
OFDM_M, OFDM_CP, OFDM_TAPER, NULL, callback, NULL);
|
||||
if (!fs) {
|
||||
fprintf(stderr, "Ошибка создания OFDM-синхронизатора\n");
|
||||
if (decoder) fec_destroy(decoder);
|
||||
if (nco) nco_crcf_destroy(nco);
|
||||
iio_buffer_destroy(buf);
|
||||
iio_context_destroy(ctx);
|
||||
return 1;
|
||||
@@ -163,8 +180,8 @@ int main(int argc, char *argv[]) {
|
||||
ofdmflexframesync_set_header_len(fs, HDR_SIZE);
|
||||
|
||||
fprintf(stderr, "Приёмник готов. Ожидание сигнала...\n\n");
|
||||
fprintf(stderr, "Кадры | Загол. | CRC | RS испр | Вывод | RSSI | EVM | CFO\n");
|
||||
fprintf(stderr, "--------+--------+------+---------+--------+-------+-------+----------\n");
|
||||
fprintf(stderr, "Кадры | Загол. | CRC | RS испр | RS бит | Потери | Вывод | RSSI | EVM | CFO\n");
|
||||
fprintf(stderr, "--------+--------+------+---------+--------+--------+--------+-------+-------+----------\n");
|
||||
|
||||
time_t last = time(NULL);
|
||||
|
||||
@@ -179,19 +196,15 @@ int main(int argc, char *argv[]) {
|
||||
for (int i = 0; i < ns; i++) {
|
||||
frame[i] = ((float)bb[2*i] / 2048.0f) +
|
||||
((float)bb[2*i+1] / 2048.0f) * _Complex_I;
|
||||
if (nco) {
|
||||
nco_crcf_mix_down(nco, frame[i], &frame[i]);
|
||||
nco_crcf_step(nco);
|
||||
}
|
||||
}
|
||||
if (nco) nco_crcf_set_phase(nco, 0.0f);
|
||||
|
||||
ofdmflexframesync_execute(fs, frame, ns);
|
||||
|
||||
if (time(NULL) > last) {
|
||||
fprintf(stderr, "\r%6llu | %5llu | %4llu | %6llu | %6llu | %+5.0f | %+5.1f | %+8.4f",
|
||||
fprintf(stderr, "\r%6llu | %5llu | %4llu | %6llu | %5llu | %6llu | %6llu | %+5.0f | %+5.1f | %+8.4f",
|
||||
(unsigned long long)calls, (unsigned long long)hdr_ok,
|
||||
(unsigned long long)crc_raw, (unsigned long long)rs_saved,
|
||||
(unsigned long long)rs_fail, (unsigned long long)lost,
|
||||
(unsigned long long)crc_final, rssi, evm, cfo_est);
|
||||
fflush(stderr);
|
||||
last = time(NULL);
|
||||
@@ -199,13 +212,13 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n\nПРИЁМ ЗАВЕРШЁН\n");
|
||||
fprintf(stderr, "Кадров: %llu | Заголовков: %llu | CRC: %llu | RS испр: %llu | Вывод: %llu | Байт: %llu\n",
|
||||
fprintf(stderr, "Кадров: %llu | Заголовков: %llu | CRC: %llu | RS испр: %llu | RS битых: %llu | Потери seq: %llu | Вывод: %llu | Байт: %llu\n",
|
||||
(unsigned long long)calls, (unsigned long long)hdr_ok,
|
||||
(unsigned long long)crc_raw, (unsigned long long)rs_saved,
|
||||
(unsigned long long)rs_fail, (unsigned long long)lost,
|
||||
(unsigned long long)crc_final, (unsigned long long)total_bytes);
|
||||
|
||||
if (decoder) fec_destroy(decoder);
|
||||
if (nco) nco_crcf_destroy(nco);
|
||||
ofdmflexframesync_destroy(fs);
|
||||
iio_buffer_destroy(buf);
|
||||
iio_context_destroy(ctx);
|
||||
|
||||
@@ -120,24 +120,36 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// Кодирование данных
|
||||
if (enc) {
|
||||
size_t total_blocks = (n + RS_DATA - 1) / RS_DATA;
|
||||
// CRC32 поверх исходных данных ДО RS: fec_decode() liquid не
|
||||
// сообщает о неисправимых блоках, поэтому единственный надёжный
|
||||
// критерий целостности на RX — этот CRC поверх декода (техдолг #3).
|
||||
uint8_t framed[MAX_PAYLOAD + 4];
|
||||
memcpy(framed, raw, n);
|
||||
unsigned int crc = crc_generate_key(LIQUID_CRC_32, raw, n);
|
||||
framed[n] = (crc >> 24) & 0xFF;
|
||||
framed[n + 1] = (crc >> 16) & 0xFF;
|
||||
framed[n + 2] = (crc >> 8) & 0xFF;
|
||||
framed[n + 3] = crc & 0xFF;
|
||||
size_t framed_n = n + 4;
|
||||
|
||||
size_t total_blocks = (framed_n + RS_DATA - 1) / RS_DATA;
|
||||
tx_len = total_blocks * RS_ENC;
|
||||
tx_payload = malloc(tx_len);
|
||||
|
||||
for (size_t b = 0; b < total_blocks; b++) {
|
||||
uint8_t blk[RS_DATA] = {0};
|
||||
size_t bytes = (b == total_blocks - 1) ? n - b * RS_DATA : RS_DATA;
|
||||
memcpy(blk, raw + b * RS_DATA, bytes);
|
||||
size_t bytes = (b == total_blocks - 1) ? framed_n - b * RS_DATA : RS_DATA;
|
||||
memcpy(blk, framed + b * RS_DATA, bytes);
|
||||
fec_encode(enc, RS_DATA, blk, tx_payload + b * RS_ENC);
|
||||
}
|
||||
|
||||
// Формирование заголовка
|
||||
// Заголовок: len = исходные n (без 4 байт CRC), nblocks/last по framed
|
||||
hdr[0] = HDR_SIGNATURE_0; hdr[1] = HDR_SIGNATURE_1;
|
||||
hdr[2] = (seq >> 24) & 0xFF; hdr[3] = (seq >> 16) & 0xFF;
|
||||
hdr[4] = (seq >> 8) & 0xFF; hdr[5] = seq & 0xFF;
|
||||
hdr[6] = (n >> 8) & 0xFF; hdr[7] = n & 0xFF;
|
||||
hdr[8] = (total_blocks >> 8) & 0xFF; hdr[9] = total_blocks & 0xFF;
|
||||
hdr[10] = n % RS_DATA; hdr[11] = 0;
|
||||
hdr[10] = framed_n % RS_DATA; hdr[11] = 0;
|
||||
} else {
|
||||
tx_len = n;
|
||||
tx_payload = malloc(tx_len);
|
||||
@@ -167,7 +179,10 @@ int main(int argc, char *argv[]) {
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
if (idx > 0) iio_buffer_push(buf);
|
||||
// Пушим только фактически заполненные idx сэмплов, а не весь буфер
|
||||
// 16384 (техдолг #5): иначе ~35% мёртвого эфира на кадр и сбитый
|
||||
// межкадровый тайминг. push_partial даёт ту же длину, что write.
|
||||
if (idx > 0) iio_buffer_push_partial(buf, idx);
|
||||
|
||||
// Обновление статистики
|
||||
frames++; bytes_tx += n; seq++;
|
||||
@@ -183,6 +198,15 @@ int main(int argc, char *argv[]) {
|
||||
fprintf(stderr, "\nПередача завершена: %llu кадров, %llu байт\n",
|
||||
(unsigned long long)frames, (unsigned long long)bytes_tx);
|
||||
|
||||
// Дренаж: выпихнуть последний кадр из kernel-буферов DMA перед закрытием.
|
||||
// Без паузы iio_buffer_destroy() срезает хвост файла в эфире (техдолг #5).
|
||||
{
|
||||
int16_t *out = (int16_t *)iio_buffer_start(buf);
|
||||
memset(out, 0, BUF_SAMPLES * 2 * sizeof(int16_t));
|
||||
iio_buffer_push(buf);
|
||||
usleep(100000); // ~4 kernel-буфера при 1.92 MSPS ≈ 35 мс, с запасом
|
||||
}
|
||||
|
||||
// Освобождение ресурсов
|
||||
if (enc) fec_destroy(enc);
|
||||
ofdmflexframegen_destroy(fg);
|
||||
|
||||
Reference in New Issue
Block a user