From 49e6c8f9406d477beeabef33ea5704c3646e7965 Mon Sep 17 00:00:00 2001 From: Maxim Date: Thu, 16 Jul 2026 08:16:04 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=BC=D0=B5=D0=B6=D1=83?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BD=D1=8B=D0=B5=20=D0=B8=D0=B7=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- makefile | 4 +-- src/common.c | 8 +++++ src/common.h | 12 +++++++ src/frame_tx.c | 70 +++++++++++++++++++++++++++++++++++++++++ src/frame_tx.h | 20 ++++++++++++ src/receiver.c | 62 +++++++++++++++++++++---------------- src/transmitter.c | 79 +++++------------------------------------------ 7 files changed, 156 insertions(+), 99 deletions(-) create mode 100644 src/frame_tx.c create mode 100644 src/frame_tx.h diff --git a/makefile b/makefile index 85011bb..0982f07 100644 --- a/makefile +++ b/makefile @@ -25,8 +25,8 @@ all: $(BIN)/transmitter $(BIN)/receiver $(BIN)/ofdm_bench $(BIN): mkdir -p $(BIN) -$(BIN)/transmitter: $(SRC)/transmitter.c $(SRC)/common.c $(SRC)/group_fec.c $(SRC)/common.h $(SRC)/group_fec.h $(DEPS_LIBS) | $(BIN) - $(CC) $(CFLAGS) $(SRC)/transmitter.c $(SRC)/common.c $(SRC)/group_fec.c -o $@ $(LDFLAGS) -Wl,--start-group -lad9361 -liio $(LDLIBS) -Wl,--end-group +$(BIN)/transmitter: $(SRC)/transmitter.c $(SRC)/common.c $(SRC)/group_fec.c $(SRC)/frame_tx.c $(SRC)/common.h $(SRC)/group_fec.h $(SRC)/frame_tx.h $(DEPS_LIBS) | $(BIN) + $(CC) $(CFLAGS) $(SRC)/transmitter.c $(SRC)/common.c $(SRC)/group_fec.c $(SRC)/frame_tx.c -o $@ $(LDFLAGS) -Wl,--start-group -lad9361 -liio $(LDLIBS) -Wl,--end-group $(BIN)/receiver: $(SRC)/receiver.c $(SRC)/common.c $(SRC)/group_fec.c $(SRC)/common.h $(SRC)/group_fec.h $(DEPS_LIBS) | $(BIN) $(CC) $(CFLAGS) $(SRC)/receiver.c $(SRC)/common.c $(SRC)/group_fec.c -o $@ $(LDFLAGS) -Wl,--start-group -lad9361 -liio $(LDLIBS) -Wl,--end-group diff --git a/src/common.c b/src/common.c index 316320e..54954a7 100644 --- a/src/common.c +++ b/src/common.c @@ -63,6 +63,14 @@ int pluto_init(const char *uri, struct iio_context **ctx, return 0; } +void pin_to_cpu(int cpu) { + cpu_set_t cs; + CPU_ZERO(&cs); + CPU_SET(cpu, &cs); + if (pthread_setaffinity_np(pthread_self(), sizeof(cs), &cs) != 0) + fprintf(stderr, "[warn] не удалось привязать поток к ядру %d\n", cpu); +} + int pluto_configure(struct iio_device *phy, long long freq, long long rate, long long bw, double gain, int is_tx) { // Частота дискретизации через libad9361: функция сама рассчитывает и diff --git a/src/common.h b/src/common.h index eb7fb35..4281888 100644 --- a/src/common.h +++ b/src/common.h @@ -6,6 +6,12 @@ #ifndef COMMON_H #define COMMON_H +// pthread_setaffinity_np/cpu_set_t (pin_to_cpu) — должен быть определён ДО +// первого системного заголовка, поэтому здесь, перед stdio.h и т.п. +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include @@ -15,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -60,4 +68,8 @@ int pluto_init(const char *uri, struct iio_context **ctx, int pluto_configure(struct iio_device *phy, long long freq, long long rate, long long bw, double gain, int is_tx); +// Привязка вызывающего потока к ядру CPU (доктрина README §2: ядро 0 — только +// sync, ядро 1 — всё остальное). Ошибка не фатальна — планировщик разведёт сам. +void pin_to_cpu(int cpu); + #endif \ No newline at end of file diff --git a/src/frame_tx.c b/src/frame_tx.c new file mode 100644 index 0000000..d3f17bb --- /dev/null +++ b/src/frame_tx.c @@ -0,0 +1,70 @@ +// frame_tx.c — реализация phy_tx_frame (вынесено из transmitter.c, этап 0 +// roadmap §10 п.7: общий путь кодирования нужен и обратному каналу). + +#include "frame_tx.h" +#include "group_fec.h" + +void phy_tx_frame(ofdmflexframegen fg, struct iio_buffer *buf, int buf_samples, + fec enc, const uint8_t sig[2], uint32_t seq, uint8_t hdr11, + const uint8_t *pl, size_t n, float amp, int pause_us) { + uint8_t *tx_payload = NULL; + size_t tx_len = 0; + uint8_t hdr[HDR_SIZE] = {0}; + + if (enc) { + // CRC32 поверх исходных данных ДО RS (техдолг #3). framed[] вмещает + // самый большой легитимный payload — паритетный кадр GF_PAR_PAY. + uint8_t framed[GF_PAR_PAY + 4]; + memcpy(framed, pl, n); + // liquid crc_generate_key() не помечает вход const (API-огрех), хотя + // только читает буфер — снимаем const кастом, чтобы -Wextra был чист. + unsigned int crc = crc_generate_key(LIQUID_CRC_32, (unsigned char *)pl, 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) ? 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); + } + hdr[8] = (total_blocks >> 8) & 0xFF; hdr[9] = total_blocks & 0xFF; + hdr[10] = framed_n % RS_DATA; + } else { + tx_len = n; + tx_payload = malloc(tx_len); + memcpy(tx_payload, pl, n); + } + + hdr[0] = sig[0]; hdr[1] = sig[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[11] = hdr11; // group-FEC флаги (0 = legacy) + + ofdmflexframegen_assemble(fg, hdr, tx_payload, tx_len); + free(tx_payload); + + int16_t *out = (int16_t *)iio_buffer_start(buf); + memset(out, 0, (size_t)buf_samples * 2 * sizeof(int16_t)); + int idx = 0, done = 0; + float complex symb[OFDM_M + OFDM_CP]; + while (!done && (idx + OFDM_M + OFDM_CP) <= buf_samples) { + done = ofdmflexframegen_write(fg, symb, OFDM_M + OFDM_CP); + for (int i = 0; i < OFDM_M + OFDM_CP; i++) { + out[2 * idx] = (int16_t)(crealf(symb[i]) * 32767 * amp); + out[2 * idx + 1] = (int16_t)(cimagf(symb[i]) * 32767 * amp); + idx++; + } + } + // Только фактически заполненные idx сэмплов (техдолг #5): иначе мёртвый эфир + // и сбитый межкадровый тайминг. push_partial даёт ту же длину, что write. + if (idx > 0) iio_buffer_push_partial(buf, idx); + if (pause_us > 0) usleep(pause_us); +} diff --git a/src/frame_tx.h b/src/frame_tx.h new file mode 100644 index 0000000..ada2bb6 --- /dev/null +++ b/src/frame_tx.h @@ -0,0 +1,20 @@ +// frame_tx.h — сборка и передача одного OFDM-кадра (общий путь для TX данных +// и, начиная с этапа 2 roadmap §10 п.7, обратного канала STATUS/END). + +#ifndef FRAME_TX_H +#define FRAME_TX_H + +#include "common.h" + +// Кодирует и передаёт один кадр: если enc задан — CRC32 поверх исходных данных +// ДО RS (техдолг #3: fec_decode() liquid не сообщает об ошибках, это единственный +// надёжный критерий на RX), затем блочный RS(255,223); иначе payload как есть. +// sig[0..1] — байты 0-1 заголовка (различают тип кадра: данные/паритет F0 AA, +// END F0 E7, STATUS F0 55 — см. docs/NOTES.md §10 п.7). hdr11 — байт флагов +// group-FEC (0 = legacy). buf_samples — размер TX-буфера DMA (BUF_SAMPLES +// вызывающего). Кадр пишется частичным push_partial (техдолг #5), затем пауза. +void phy_tx_frame(ofdmflexframegen fg, struct iio_buffer *buf, int buf_samples, + fec enc, const uint8_t sig[2], uint32_t seq, uint8_t hdr11, + const uint8_t *pl, size_t n, float amp, int pause_us); + +#endif diff --git a/src/receiver.c b/src/receiver.c index 2c66235..b8d3cea 100644 --- a/src/receiver.c +++ b/src/receiver.c @@ -7,11 +7,8 @@ // Замер: sync сам 1.65 Msps < 1.92, RS 26% эфира. Вынос RS на ядро 1 роняет // p_min ~8 мс → ~1.5–2 мс. -p 0 на этом PHY недостижим (осознанно, §12.5). -#define _GNU_SOURCE // pthread_setaffinity_np, cpu_set_t -#include "common.h" +#include "common.h" // _GNU_SOURCE/pthread/sched — уже подключены здесь #include "group_fec.h" -#include -#include #define BUF_SAMPLES 32768 #define RING_SLOTS 16 // 16 × 17 мс = 273 мс поглощения всплесков @@ -72,7 +69,8 @@ static float rssi = -100, evm = 0, cfo_est = 0.0f; static uint64_t rs_saved = 0, crc_final = 0, total_bytes = 0; static uint64_t rs_fail = 0, lost = 0; static uint64_t grp_recovered = 0, grp_failed = 0; // group-FEC: восст. кадров / провал. групп -static uint32_t last_seq = 0; +static uint64_t sig_bad = 0; // hdr[0..1] не совпал с известной сигнатурой +static uint32_t highest_seq = 0; // максимальный увиденный seq (не откатывается назад) static int seq_init = 0; // Разделяемых на запись счётчиков нет. main читает их раз в секунду для // телеметрии без синхронизации: на 32-бит ARM возможен косметический «рваный» @@ -221,19 +219,36 @@ static void process_frame(fq_slot_t *fs) { unsigned pay_len = fs->pay_len; int pay_valid = fs->pay_valid; - // Детектор пропусков seq (техдолг #6). Кадр, выброшенный переполнением fq, + // Проверка сигнатуры hdr[0..1]: liquid-CRC (hdr_valid) страхует только + // целостность байт, но не то, что это наш протокол. Пока в эфире один тип + // кадра (данные/паритет GF, F0 AA) — это no-op; готовит почву под доп. + // типы кадров обратного канала (roadmap §10 п.7: STATUS/END на др. частоте). + if (hdr[0] != HDR_SIGNATURE_0 || hdr[1] != HDR_SIGNATURE_1) { + sig_bad++; + return; + } + + // Детектор пропусков seq (техдолг #6) на основе МАКСИМАЛЬНОГО увиденного + // seq, а не последнего: повтор/переупорядоченный кадр с seq ≤ highest_seq + // (ретрансмит ARQ, §10 п.7) не должен откатывать состояние назад и рождать + // ложную дыру на следующем свежем кадре. Кадр, выброшенный переполнением fq, // тоже проявится здесь как дыра seq → в `lost`; счётчик FQdrop разделяет // причины (эфирная потеря против переполнения очереди). В group-FEC потеря // одновременно фиксируется как стирание в group_route по idx. 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; + if (!seq_init) { + seq_init = 1; + highest_seq = seq; + } else if (seq > highest_seq + 1) { + lost += seq - highest_seq - 1; fprintf(stderr, "\n[ПОТЕРЯ] seq %u→%u, дыра %u кадров\n", - last_seq, seq, seq - last_seq - 1); + highest_seq, seq, seq - highest_seq - 1); + highest_seq = seq; + } else if (seq > highest_seq) { + highest_seq = seq; } - last_seq = seq; - seq_init = 1; + // seq ≤ highest_seq: старый/повторный кадр — highest_seq не трогаем. uint16_t original_len = (hdr[6] << 8) | hdr[7]; uint8_t last_block_bytes = hdr[10]; @@ -297,15 +312,8 @@ route: } } -// Привязка потока к ядру: ядро 0 — только sync, ядро 1 — захват+FEC+IO -// (доктрина README §2). Ошибка не фатальна — планировщик разведёт сам. -static void pin_to_cpu(int cpu) { - cpu_set_t cs; - CPU_ZERO(&cs); - CPU_SET(cpu, &cs); - if (pthread_setaffinity_np(pthread_self(), sizeof(cs), &cs) != 0) - fprintf(stderr, "[warn] не удалось привязать поток к ядру %d\n", cpu); -} +// pin_to_cpu — теперь в common.{h,c} (используется также будущим потоком +// обратного канала на TX, roadmap §10 п.7). // Ждать данных в SPSC-кольце (100 мс страховка от lost wakeup). Возврат: // 1 — есть слот (мьютекс m ЗАБЛОКИРОВАН на выходе), 0 — eof/stop и пусто (m разблокирован). @@ -491,8 +499,8 @@ int main(int argc, char *argv[]) { ofdmflexframesync_set_header_len(fs, HDR_SIZE); fprintf(stderr, "Приёмник готов. Ожидание сигнала...\n\n"); - fprintf(stderr, "Кадры | Загол. | CRC | RS испр | RS бит | Потери | Overr | FQdrp | Rec | GrpF | Вывод | RSSI | EVM | CFO\n"); - fprintf(stderr, "--------+--------+------+---------+--------+--------+--------+--------+--------+--------+--------+-------+-------+----------\n"); + fprintf(stderr, "Кадры | Загол. | CRC | RS испр | RS бит | SigBd | Потери | Overr | FQdrp | Rec | GrpF | Вывод | RSSI | EVM | CFO\n"); + fprintf(stderr, "--------+--------+------+---------+--------+-------+--------+--------+--------+--------+--------+--------+-------+-------+----------\n"); // Три стадии на двух ядрах. main крутит статистику 1 Гц и ждёт stop, затем // join A→B→C: A ставит ring.eof, B дочищает кольцо и ставит fq.eof, C @@ -510,10 +518,11 @@ int main(int argc, char *argv[]) { nanosleep(&nap, NULL); time_t now = time(NULL); if (now > last) { - fprintf(stderr, "\r%6llu | %5llu | %4llu | %6llu | %5llu | %6llu | %6llu | %6llu | %6llu | %6llu | %6llu | %+5.0f | %+5.1f | %+8.4f", + fprintf(stderr, "\r%6llu | %5llu | %4llu | %6llu | %5llu | %5llu | %6llu | %6llu | %6llu | %6llu | %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)rs_fail, (unsigned long long)sig_bad, + (unsigned long long)lost, (unsigned long long)ring.overruns, (unsigned long long)fq.drops, (unsigned long long)grp_recovered, (unsigned long long)grp_failed, (unsigned long long)crc_final, rssi, evm, cfo_est); @@ -527,10 +536,11 @@ int main(int argc, char *argv[]) { pthread_join(tc, NULL); fprintf(stderr, "\n\nПРИЁМ ЗАВЕРШЁН\n"); - fprintf(stderr, "Кадров: %llu | Заголовков: %llu | CRC: %llu | RS испр: %llu | RS битых: %llu | Потери seq: %llu | Overrun: %llu | FQ drops: %llu | GF восст: %llu | GF провал: %llu | Вывод: %llu | Байт: %llu\n", + fprintf(stderr, "Кадров: %llu | Заголовков: %llu | CRC: %llu | RS испр: %llu | RS битых: %llu | Сигн.битых: %llu | Потери seq: %llu | Overrun: %llu | FQ drops: %llu | GF восст: %llu | GF провал: %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)rs_fail, (unsigned long long)sig_bad, + (unsigned long long)lost, (unsigned long long)ring.overruns, (unsigned long long)fq.drops, (unsigned long long)grp_recovered, (unsigned long long)grp_failed, (unsigned long long)crc_final, (unsigned long long)total_bytes); diff --git a/src/transmitter.c b/src/transmitter.c index 1e1abb7..4549c98 100644 --- a/src/transmitter.c +++ b/src/transmitter.c @@ -7,6 +7,7 @@ #include "common.h" #include "group_fec.h" +#include "frame_tx.h" // Параметры передатчика по умолчанию #define DEFAULT_GAIN -30.0 // Усиление передатчика: -30 дБ @@ -18,73 +19,9 @@ // Внешняя переменная флага завершения (определена в common.c) extern volatile sig_atomic_t stop; -// Кодирование и отправка одного кадра: CRC-поверх-RS (техдолг #3) → заголовок -// 12 Б → OFDM-сборка → push_partial → пауза. hdr11 несёт флаги group-FEC -// (0 для legacy). pl/n — полезная нагрузка (данные ≤1024 или паритет 1026). -static void tx_frame(ofdmflexframegen fg, struct iio_buffer *buf, fec enc, - uint32_t seq, uint8_t hdr11, - const uint8_t *pl, size_t n, float amp, int pause_us) { - uint8_t *tx_payload = NULL; - size_t tx_len = 0; - uint8_t hdr[HDR_SIZE] = {0}; - - if (enc) { - // CRC32 поверх исходных данных ДО RS: fec_decode() liquid не сообщает о - // неисправимых блоках — единственный надёжный критерий на RX (техдолг #3). - uint8_t framed[GF_PAR_PAY + 4]; // ≥ данные+4 и ≥ паритет 1026+4 - memcpy(framed, pl, n); - // liquid crc_generate_key() не помечает вход const (API-огрех), хотя - // только читает буфер — снимаем const кастом, чтобы -Wextra был чист. - unsigned int crc = crc_generate_key(LIQUID_CRC_32, (unsigned char *)pl, 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) ? 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); - } - hdr[8] = (total_blocks >> 8) & 0xFF; hdr[9] = total_blocks & 0xFF; - hdr[10] = framed_n % RS_DATA; - } else { - tx_len = n; - tx_payload = malloc(tx_len); - memcpy(tx_payload, pl, n); - } - - 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[11] = hdr11; // group-FEC флаги (0 = legacy) - - ofdmflexframegen_assemble(fg, hdr, tx_payload, tx_len); - free(tx_payload); - - int16_t *out = (int16_t *)iio_buffer_start(buf); - memset(out, 0, BUF_SAMPLES * 2 * sizeof(int16_t)); - int idx = 0, done = 0; - float complex symb[OFDM_M + OFDM_CP]; - while (!done && (idx + OFDM_M + OFDM_CP) <= BUF_SAMPLES) { - done = ofdmflexframegen_write(fg, symb, OFDM_M + OFDM_CP); - for (int i = 0; i < OFDM_M + OFDM_CP; i++) { - out[2 * idx] = (int16_t)(crealf(symb[i]) * 32767 * amp); - out[2 * idx + 1] = (int16_t)(cimagf(symb[i]) * 32767 * amp); - idx++; - } - } - // Только фактически заполненные idx сэмплов (техдолг #5): иначе мёртвый эфир - // и сбитый межкадровый тайминг. push_partial даёт ту же длину, что write. - if (idx > 0) iio_buffer_push_partial(buf, idx); - if (pause_us > 0) usleep(pause_us); -} +// Сигнатура кадров данных/паритета GF (см. frame_tx.h — тип кадра различает +// hdr[0..1]; END/STATUS обратного канала используют другие сигнатуры, §10 п.7). +static const uint8_t SIG_DATA[2] = { HDR_SIGNATURE_0, HDR_SIGNATURE_1 }; int main(int argc, char *argv[]) { // Инициализация параметров @@ -188,7 +125,7 @@ int main(int argc, char *argv[]) { usleep(10000); continue; } - tx_frame(fg, buf, enc, seq++, 0, raw, n, amp, pause_us); + phy_tx_frame(fg, buf, BUF_SAMPLES, enc, SIG_DATA, seq++, 0, raw, n, amp, pause_us); frames++; bytes_tx += n; if (time(NULL) > last) { fprintf(stderr, "\r📤 Передано: %llu кадров, %llu байт", @@ -216,7 +153,7 @@ int main(int argc, char *argv[]) { if (k == GF_K || (done_reading && k > 0)) { // k кадров данных: idx=0..k-1 for (int i = 0; i < k; i++) { - tx_frame(fg, buf, enc, seq++, GF_FLAG_MODE | i, + phy_tx_frame(fg, buf, BUF_SAMPLES, enc, SIG_DATA, seq++, GF_FLAG_MODE | i, gdata[i], glen[i], amp, pause_us); frames++; bytes_tx += glen[i]; } @@ -226,10 +163,10 @@ int main(int argc, char *argv[]) { uint16_t meta = GF_META_PACK(k, glen[k - 1]); par[0] = meta >> 8; par[1] = meta & 0xFF; memcpy(par + GF_META, P, GF_CHUNK); - tx_frame(fg, buf, enc, seq++, GF_FLAG_MODE | GF_FLAG_PAR | k, + phy_tx_frame(fg, buf, BUF_SAMPLES, enc, SIG_DATA, seq++, GF_FLAG_MODE | GF_FLAG_PAR | k, par, GF_PAR_PAY, amp, pause_us); memcpy(par + GF_META, Q, GF_CHUNK); - tx_frame(fg, buf, enc, seq++, GF_FLAG_MODE | GF_FLAG_PAR | GF_FLAG_Q | (k + 1), + phy_tx_frame(fg, buf, BUF_SAMPLES, enc, SIG_DATA, seq++, GF_FLAG_MODE | GF_FLAG_PAR | GF_FLAG_Q | (k + 1), par, GF_PAR_PAY, amp, pause_us); frames += 2; k = 0;