Этап 2. Новый модуль обратного канала

This commit is contained in:
Maxim
2026-07-16 11:21:14 +03:00
parent b5bf0c7b18
commit 341a5555ab
10 changed files with 428 additions and 9 deletions

View File

@@ -8,6 +8,7 @@
#include "common.h"
#include "group_fec.h"
#include "frame_tx.h"
#include "feedback.h" // приём STATUS обратного канала (этап 2, §10 п.7)
// Параметры передатчика по умолчанию
#define DEFAULT_GAIN -30.0 // Усиление передатчика: -30 дБ
@@ -24,6 +25,63 @@ extern volatile sig_atomic_t stop;
// hdr[0..1]; END/STATUS обратного канала используют другие сигнатуры, §10 п.7).
static const uint8_t SIG_DATA[2] = { HDR_SIGNATURE_0, HDR_SIGNATURE_1 };
// --- Обратный канал: приём STATUS на 868 (этап 2, §10 п.7) --------------
// Отдельный поток на ядре 1 (данные TX — ядро 0). Свой синхронизатор с
// header_len 12 и свой RS-декодер. Пока ТОЛЬКО логирует; ARQ-реакция (ретрансмит
// по NACK) — этап 3. Слушает rxfb — второй cf-девайс, открытый pluto_init_fdd.
#define FB_RX_BUF_SAMPLES 16384
static struct iio_buffer *g_fbrx_buf = NULL;
static ofdmflexframesync g_fbrx_fs = NULL;
static fec g_fbrx_dec = NULL;
static volatile sig_atomic_t fb_stop = 0;
static uint64_t g_fb_status_rx = 0; // сколько STATUS декодировано (для итога)
// callback синхронизатора обратного канала: отсев по сигнатуре F0 55, RS+CRC
// декод, распаковка и лог. Этап 2 — без действий над состоянием передачи.
static int fb_callback(unsigned char *hdr, int hdr_valid,
unsigned char *pay, unsigned int pay_len,
int pay_valid, framesyncstats_s stats, void *user) {
(void)pay_valid; (void)stats; (void)user;
if (!hdr_valid) return 0;
if (hdr[0] != FB_SIG_0 || hdr[1] != FB_SIG_1) return 0; // не STATUS
static uint8_t dec[RS_ENC * 2]; // STATUS ≤ 1 RS-блок, запас на будущее
size_t dlen = 0;
if (!phy_rx_decode(hdr, pay, pay_len, g_fbrx_dec, dec, sizeof(dec), &dlen))
return 0; // RS/CRC не прошли — молча дропаем
fb_status_t st;
if (fb_status_unpack(dec, dlen, &st) != 0) return 0;
g_fb_status_rx++;
fprintf(stderr, "\n[FB] highest=%u rx_frames=%u overruns=%u nack=%u{",
st.highest_seq, st.rx_frames, st.overruns, st.nack_cnt);
for (int i = 0; i < st.nack_cnt && i < 8; i++)
fprintf(stderr, "%s%u", i ? "," : "", st.nack[i]);
if (st.nack_cnt > 8) fprintf(stderr, ",…");
fprintf(stderr, "}\n");
return 0;
}
static void *fb_rx_thread(void *arg) {
(void)arg;
pin_to_cpu(1);
static liquid_float_complex conv[FB_RX_BUF_SAMPLES];
while (!fb_stop && !stop) {
ssize_t nb = iio_buffer_refill(g_fbrx_buf);
if (nb < 0) { if (fb_stop || stop) break; usleep(500); continue; }
int16_t *bb = (int16_t *)iio_buffer_start(g_fbrx_buf);
int ns = (iio_buffer_end(g_fbrx_buf) - iio_buffer_start(g_fbrx_buf))
/ (int)sizeof(int16_t) / 2;
if (ns > FB_RX_BUF_SAMPLES) ns = FB_RX_BUF_SAMPLES;
for (int i = 0; i < ns; i++)
conv[i] = ((float)bb[2*i] / 2048.0f) +
((float)bb[2*i+1] / 2048.0f) * _Complex_I;
ofdmflexframesync_execute(g_fbrx_fs, conv, ns);
}
return NULL;
}
int main(int argc, char *argv[]) {
// Инициализация параметров
long long freq = DEFAULT_FREQ, rate = DEFAULT_RATE, bw = DEFAULT_BW;
@@ -74,7 +132,7 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Пауза: %d мкс\n", pause_us);
fprintf(stderr, "Group-FEC: %s\n", group_fec ? "16+2 (§10 п.6)" : "выкл");
fprintf(stderr, "Дуплекс: %s\n", fb_freq
? "FDD (обратный RX настроен, трафик — этап 2)" : "симплекс");
? "FDD (обратный канал: приём STATUS на 868)" : "симплекс");
if (fb_freq) fprintf(stderr, "Обр. RX LO: %.1f МГц\n", fb_freq / 1e6);
fprintf(stderr, "URI: %s\n", uri);
fprintf(stderr, "═══════════════════════════════════════════════════\n");
@@ -122,6 +180,29 @@ int main(int argc, char *argv[]) {
// last_block_bytes) без этого в эфир не уходят. RX выставляет то же.
ofdmflexframegen_set_header_len(fg, HDR_SIZE);
// Обратный канал (этап 2): поток fb_rx слушает STATUS на 868 (rxfb). Пока
// только логирует. Данные TX уводим на ядро 0, fb_rx — на ядро 1 (§affinity).
pthread_t fb_tid;
int fb_started = 0;
if (fb_freq) {
iio_channel_enable(iio_device_find_channel(rxfb, "voltage0", false));
iio_channel_enable(iio_device_find_channel(rxfb, "voltage1", false));
g_fbrx_buf = iio_device_create_buffer(rxfb, FB_RX_BUF_SAMPLES, false);
if (!g_fbrx_buf) {
LOG_ERROR("Буфер обратного канала не создан — STATUS не принимаются");
} else {
g_fbrx_fs = ofdmflexframesync_create(OFDM_M, OFDM_CP, OFDM_TAPER,
NULL, fb_callback, NULL);
ofdmflexframesync_set_header_len(g_fbrx_fs, HDR_SIZE);
g_fbrx_dec = fec_create(LIQUID_FEC_RS_M8, NULL);
pin_to_cpu(0); // TX-данные — ядро 0
pthread_create(&fb_tid, NULL, fb_rx_thread, NULL);
fb_started = 1;
fprintf(stderr, "Обратный канал: слушаю STATUS на %.1f МГц (ядро 1)\n",
fb_freq / 1e6);
}
}
// Счётчики статистики
uint64_t frames = 0, bytes_tx = 0;
uint32_t seq = 0;
@@ -207,10 +288,22 @@ int main(int argc, char *argv[]) {
usleep(100000); // ~4 kernel-буфера при 1.92 MSPS ≈ 35 мс, с запасом
}
// Остановить обратный канал: fb_rx выходит по fb_stop в течение одного
// refill (~8.5 мс на 16384 сэмпл @ 1.92 Msps), затем join.
if (fb_started) {
fb_stop = 1;
pthread_join(fb_tid, NULL);
fprintf(stderr, "Обратный канал: принято STATUS: %llu\n",
(unsigned long long)g_fb_status_rx);
}
// Освобождение ресурсов
if (enc) fec_destroy(enc);
ofdmflexframegen_destroy(fg);
iio_buffer_destroy(buf);
if (g_fbrx_fs) ofdmflexframesync_destroy(g_fbrx_fs);
if (g_fbrx_dec) fec_destroy(g_fbrx_dec);
if (g_fbrx_buf) iio_buffer_destroy(g_fbrx_buf);
iio_context_destroy(ctx);
return 0;
}